/**
 * Created by hjqi on 2016/4/8.
 */

//third part
var querystring = require('querystring');
var K8s = require('k8s');
var promiseWhile = require('promise-while')(Promise);

//config part
var config = require('../config/config').config;
var _commandMap = config.commands;
var _glusterfsConf = config.glusterfsMeta;
var grayUdpIntervalSec = 10;

//common part
var httpRequest = require('../common/httpRequest').httpRequest;
var _req = new httpRequest();
var Common = require('../common/common');
var _deepCopy = require('../common/common').deepCopy;
var deepcopy = require("deepcopy");
var logger = require('../log4js/log4js').logger('kubernetes');
var uuid = require('../common/common').generateUUID;
var myRet = require('../common/common').myReturn;

//db part
var appConfigHistoryDB = require('../../db/appConfigHistoryDB');
var confHistoryDB = new appConfigHistoryDB();
var serviceMetaInfoDB = require('../../db/serviceMetaInfoDB');
var svcMetaInfoDB = new serviceMetaInfoDB();
var composeMetaInfoDB = require('../../db/composeMetaInfoDB');
var compMetaInfoDB = new composeMetaInfoDB();
var storageMetaInfoDB = require('../../db/storageMetaInfoDB');
var storageInfoDB = new storageMetaInfoDB();
var Tx = require('../tx/TxManager') ,
    txManager = new Tx() ;
var Q = require('q'),
    result = Q('begin');

var isEqual = require('lodash/fp/isEqual');
var PodAutoScale = require('./k8sAutoscaling'),
    PodScale= new PodAutoScale() ;

var fs=require('fs');
var path=require('path');
var ConfigManager = require('../configManager/configManager');
var configManager=new ConfigManager();
/**
 *  change update rc mode  (old:0,new:1)
 *  old:
 *     set rc number => delete old rc => delete old service => create new service => create new rc => update db
 *  new:
 *     1.update new rc through PUT interface
 *     2.update new service through PUT interface (if port info change)
 *     3.update db
 *
 */
var updateMode = 0;

var clusterMap = {};


module.exports = libK8s;



//storage part for volume create and delete
var storageAdpt = require('../storage/storageAdapter');
var _storageHandle = storageAdpt.getStorageHandle();

function libK8s(clusterInfo) {
    init(this, clusterInfo);
}

//1.create namespace
libK8s.prototype.createNameSpace = function (ns) {
    return _createNameSpace(this, ns);
}
//2.delete namespace
libK8s.prototype.deleteNameSpace = function (ns) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        //var _url = 'http://'+config.kubernetes.host+':'+config.kubernetes.port+'/api/v1/namespaces/'+ns;
        var _url = 'http://' + _self.host + ':' + _self.port + '/api/v1/namespaces/' + ns;
        _req._request(_url, 'DELETE', {}, '', function (data, result, response) {
            if (data !== null) {
                resolve(data.data);
            }
            else {
                reject(result);
            }
        });
    });
}
//2.get info of all nodes
libK8s.prototype.getNodesInfo = function(){
    var _self = this;
    return  new Promise(function (resolve, reject) {
        var _url = 'http://'+_self.host+':'+_self.port+'/api/v1/nodes';
        _req._request(_url, 'GET',{} ,  '' , function (data, result, response) {
            if (data !== null ){
                resolve(data.data);
            }
            else{
                resolve(result);
            }
        });


    });
}

//application part
// @param  username username
//        svc   data
libK8s.prototype.createAppService = function (username, svc) {

    var _self = this;
    return new Promise(function (resolve, reject) {
        createApp(_self, username, svc).then(function () {
            resolve(true);
        }).catch(function (err) {
            logger.error("can not create app for:"+JSON.stringify( err )) ;
            reject(err);
        });
    });

}

libK8s.prototype.deleteAppService = function (username, svc,isDelVolume) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        deleteApp(_self, username, svc,isDelVolume).then(function () {
            resolve(true);
        }).catch(function (err) {
            reject(err);
        });
    });

}
libK8s.prototype.createRc=function (ns, rc,force) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        createRc(_self, ns, rc,force).then(function () {
            resolve(true);
        }).catch(function (err) {
            reject(err);
        });
    });

};
libK8s.prototype.deleteRc=function (ns, rc) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        deletePods(_self,ns,"app=" + rc.metadata.labels.app+",version="+rc.metadata.labels.version).then(function () {
            return new Promise(function(){
                return deleteRc(_self, ns, rc)
            });

        }).then(function(resolve){
            resolve(true);
        }).catch(function (err) {
            reject(err);
        });
    });

};
libK8s.prototype.publishTestVersionRc=function (ns, app) {
    var _self = this;
    return new Promise(function(resolve,reject) {
        svcMetaInfoDB.getWithParam(
            svcMetaInfoDB,
            {
                "username": ns,
                "app": app
            }, function (err, ret) {
                resolve(ret);
            }
        );
    }).then(function(ret) {
        if (ret.length >= 1) {
            var svc = ret.shift();
            var rc_new = svc.rc_test;
            var rc = rc = svc.rc;
            var old_ver = rc.metadata.labels.version;
            var new_ver = rc_new.metadata.labels.version;
            return deleteRc(_self, ns, rc).then(function () {
                return new Promise(function (resolve, reject) {
                    svcMetaInfoDB.updateWithParam(
                        svcMetaInfoDB,
                        {
                            "username": ns,
                            "app": rc.metadata.labels.app
                        },
                        {
                            $unset: {
                                rc_test: true
                            },
                            $set: {
                                rcnum: (rc_new.spec.replicas),
                                rc: rc_new,
                                ver: new_ver,
                                "node.image": svc.node.image.replace(old_ver, new_ver)
                            }
                        }, function (ret, err) {
                            deletePods(_self, ns, "app=" + app + ",version=" + old_ver, true).then(function (val) {
                                resolve(rc_new);
                            });
                        });
                });

            });
        }
    });
};
libK8s.prototype.addTestVersionRc=function (ns, rc) {
    var _self = this;

    return createRc(_self, ns,rc).then(function (val) {
        return new Promise(function(resolve,reject){
            svcMetaInfoDB.updateWithParam(svcMetaInfoDB,{"username":ns,"app":rc.metadata.labels.app},{$set:{rc_test:val,rcnum:(rc.spec.replicas)}},function(ret,err){
                resolve(val);
            });
        })
    });
};
libK8s.prototype.deleteTestVersionRc=function (ns, app) {
    var _self = this;
    svcMetaInfoDB.getWithParam(
        svcMetaInfoDB,
        {
            "username":ns,
            "app":app
        },function (err,ret) {
            if (ret.length >= 1) {
                var rc = ret.shift().rc_test;
                return deleteRc(_self, ns, rc).then(function () {
                    return new Promise(function (resolve, reject) {
                        svcMetaInfoDB.updateWithParam(
                            svcMetaInfoDB,
                            {
                                "username": ns,
                                "app": rc.metadata.labels.app
                            },
                            {
                                $unset: {
                                    rc_test: true
                                },
                                $set: {
                                    rcnum: (rc.spec.replicas)
                                }
                            }, function (ret, err) {
                                resolve(deletePods(_self, ns, "app=" + app + ",version=" + rc.metadata.labels.version, true));
                            });
                    });

                });
            }
        }
    );

};
/**
 *
 * @param ns              namespace in k8s
 * @param svc             service object
 * @param self            k8s handler
 * @param force           if force to delete pod
 * @returns {Promise}
 */
libK8s.prototype.deletePod = function(ns,svc ,self,force){
    var _self = this;
    if(self){
        _self = self ;
    }
    return new Promise(function (resolve, reject) {
        deletePods(_self,ns,"app=" + svc.app+",version="+svc.ver,force).then(function (ret) {
            resolve(ret);
        }).catch(function (err) {
            reject(err);
        });
    });
}

/**
 * 从DB取出service信息(不会去取k8s的pod信息)
 * TODO pagination
 */
libK8s.prototype.getAppServices = function(username, serviceName,clusterId){
    var _self = this;
    return new Promise(function (resolve, reject) {
        var query = {"username": username};
        if (serviceName)
            query.app = serviceName;
        if(clusterId && clusterId !== 'default'){
            query["node.clusterId"] = clusterId;
        }else{
            query["$or"] = [{"node.clusterId":{$exists:0}},{"node.clusterId":"default"}];
        }
        svcMetaInfoDB.getWithParam(null, query, function(err,docs){
            var dbDocs = docs;
            if (err) {
                logger.error("username " + username + " get service from db:" + err);
                reject(err);
                return;
            }
            if(dbDocs.length > 0){
                var list = [];
                dbDocs.forEach(function (db_obj) {
                    var l = {};
                    if (db_obj.type == undefined || db_obj.type != 'stack') {
                        l.app = db_obj.id;
                        l.id = db_obj.id;
                        l.ver = db_obj.ver;
                        var controller = db_obj.rc ? db_obj.rc : db_obj.petset;

                        l.image = db_obj.node.imageshortname;
                        l.imageurl = controller.spec.template.spec.containers[0].image;
                        var pos = l.imageurl.lastIndexOf("/");
                        if (pos != -1) {
                            var image = l.imageurl.substring(pos);
                            pos = image.lastIndexOf(":");
                            if (pos != -1) {
                                l.imageversion = image.substring(pos + 1);
                            }
                        }
                        l.replicas = controller.spec.replicas;
                        l.limit = {};
                        //add it for self host cluster
                        if(controller.spec.template.spec.containers[0].resources === undefined){
                            l.limit.cpu = undefined;
                            l.limit.memory = undefined;
                        }
                        else if (controller.spec.template.spec.containers[0].resources.request === undefined) {
                            l.limit.cpu = db_obj.node.cpu_shares;
                            l.limit.memory = db_obj.node.mem_limit;
                        } else { // 向后兼容，后面应该删掉
                            l.limit.cpu = controller.spec.template.spec.containers[0].resources.request.cpu;
                            l.limit.memory = controller.spec.template.spec.containers[0].resources.request.memory;
                        }
                        l.dbJson = db_obj;

                        //for compatible old data,set default stop, not fix old data in right status
                        if(db_obj.totalStatue === undefined)
                            db_obj.totalStatue = 'stop';

                        l.totalStatue = db_obj.totalStatue;
                        list.push(l);
                    }
                });
                resolve(list);

            }else{
                resolve([]);
            }

        });
    });
}

// below are pod auto scale operations
libK8s.prototype.CreateAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    options["param"] = {
            scaleName : request.scaleName,
            namespace : request.namespace,
            max : request.max,
            min : request.min,
            refKind : request.refKind,
            refName : request.refName,
            cpuPct : request.cpuPct
    }
    return PodScale.create(ns,options) ;
}
libK8s.prototype.ListAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    return PodScale.list(ns,options) ;
}
libK8s.prototype.DeleteAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    options["autoScalerName"] = request.scaleName||"" ;
    return PodScale.delete(ns,options) ;
}
libK8s.prototype.DeleteAllAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    return PodScale.deleteAll(ns,options) ;
}
libK8s.prototype.FindAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    options["autoScalerName"] = request.scaleName||"" ;
    return PodScale.find(ns,options) ;
}
libK8s.prototype.ReplaceAutoScale = function(request){
    var options  = this ;
    var ns = request.namespace;
    options["autoScalerName"] = request.scaleName||"" ;
    options["param"] = {
        scaleName : request.scaleName,
        namespace : request.namespace,
        max : request.max,
        min : request.min,
        refKind : request.refKind,
        refName : request.refName,
        cpuPct : request.cpuPct,
    }
    return PodScale.replace(ns,options) ;
}

var podStatusMap = {
    "Pending":"starting",
    "Running":"running",
    "Succeeded":"stop",
    "Failed":"error",
    "Terminating":"stoping",
    "Unknown":"error"
} //pod的状态对应于平台应用的状态
var containerStatusMap = {
    "Waiting":"", //stating or stopping
    "waiting":"",
    "Running":"running",
    "running":"running",
    "Terminated":"stop",
    "terminated":"stop"
} //container 状态对应于平台状态

function getState(obj){
    for (var st in obj) {
        return st;
    }
}

/**
 * 通过pod状态、容器状态来判断应用的运行状态。
 * 该应用可能有多pod，多container
 * //多个pod的状态判断：
  // 有一个pod在running，则状态为running
 * 2016/11/9
 * @Deprecated
 */
function assignPodStatus(pods ,db_obj ){
    var dbStatus = db_obj.totalStatue ;
    if (pods &&  dbStatus != "stoping") {
        var status = [];

        for (var j = 0; j < pods.length; j++) { //遍历该应用下的所有pod
            var _st = {};
            var _item = pods[j]; //pod
            var podStatus = _item.status.phase;
            var containerStatus = "" ;
            logger.debug("curr.pod.status="+ podStatus) ;
            _st.phase = podStatusMap[podStatus] || "stop" ; //default is 'stop'
            if(  podStatus!="Running" && _st.phase &&  _st.phase.indexOf("ing")!= -1 && _item.status.containerStatuses){ //进行时状态则需要判断是否处于error
                containerStatus = getState(_item.status.containerStatuses[0].state);//TODO  只取第一个容器状态?
                _st.phase = containerStatusMap[containerStatus] || _st.phase;
                logger.debug("curr.pod.containerStatus="+ containerStatus) ;
                if ( containerStatus === 'waiting' || containerStatus === 'Waiting') {

                    if (_item.status.containerStatuses[0].state['waiting'].reason !== undefined &&
                        _item.status.containerStatuses[0].state['waiting'].reason === 'CrashLoopBackOff'){
                        _st.phase = 'Error';
                    }
                    if(  dbStatus.indexOf("ing") === -1){
                        _st.phase = "stoping" ;//waiting 状态有可能是wait for stop
                    }
                    _st.lastState = _item.status.containerStatuses[0].lastState;
                }
            }
            if( ! _st.phase ){
                logger.error("[#assignPodStatus]Unknown pod status"+ podStatus) ;
            }
            status.push(_st);
        }
        var statusCounter = {
            'starting':0,
            'running':0,
            'stoping':0,
            'stop':0,
            'error':0
        };

        for (var k = 0; k < status.length; k++) {
            if( statusCounter[status[k].phase] !== undefined ){
                statusCounter[status[k].phase] ++ ;
            }else{
                statusCounter["stop"]++ ;
            }
        }
       return checkStatus( statusCounter ) ;
    }else{
        if( dbStatus != 'running' ){
            return db_obj.totalStatue ;
        }
        return "stop" ;
    }

}
/**
 * Deprecated
 */
function checkStatus(statusCounter){
    var status = "stop" ; //default status set to sop
    if( statusCounter){
        if( statusCounter["running"] > 0){
            status= "running" ;
        }else if( statusCounter["starting"] >0){
            status= "starting" ;
        }else if( statusCounter["stoping"] >0 ){
            status= "stoping" ;
        }else if( statusCounter["error"] >0 ){
            status= "error" ;
        }else if( statusCounter["stop"] >0 ){
            status= "stop" ;
        }
    }
    return status ;
}
/**
 * compare status in db with status in k8s
 * Deprecated
 */
function compareStatus(serviceInfo,result,_username,self){
    if( !serviceInfo.totalStatue||_username === "kube-system" ||  _username === "system"){
        return ;//避免把系统服务误删了
    }
    var clusterId = "default" ;
    if( serviceInfo.dbJson.node.clusterId){
        clusterId = serviceInfo.dbJson.node.clusterId ;
    }

    if(   !result || !result.data){ //pod 不存在
        if(  !serviceInfo.totalStatue.indexOf("ing")!= -1 ) {
            return ; //已经是终止状态
        }
        //数据库状态不对
        logger.warn("[#compareStatus]serviceInfo's statue mismatch k8s's status") ;
        updateServiceInfoStatus( clusterId,serviceInfo,_username, "stop" ,self) ;
        return ;
    }
    var key = serviceInfo.app+serviceInfo.ver ;
    var pod = result.data[ key ];

    if( !pod ){
        var pod = {
            status:"stop"
        } ;
    }
    if (serviceInfo.totalStatue.indexOf('ing') === -1 || serviceInfo.totalStatue === pod.status  ) {
        return ;
    } else {
        logger.warn("Pod.status and db.serviceInfo.totalStatue mismatch pod.status:"+pod.status+",serviceInfo.totalStatue:"+serviceInfo.totalStatue);
        //一个运行、停止服务的流程大致如下：
        //  请求  - > 设置为中间状态 -> 调用k8s起停服务 -> k8s将任务放入执行队列 -> 前台轮训 -> 终止态(running、stop etc) -> 更改数据库
        //可能是数据库状态处于中间态(stoping,updating,starting etc),由前台判断状态是否一致&&是否已发操作(stop,run,update etc)请求
        if( pod.status ==="pending" ){
            pod.status =serviceInfo.totalStatue;//pending状态可能为stopping或者starting
        }
        updateServiceInfoStatus( clusterId,serviceInfo,_username, pod.status ,self) ;
    }
    delete  result.data[ key ]; //remove the data from the map
}
/**
 * Deprecated
 */
function updateServiceInfoStatus( clusterId,serviceInfo,_username,status,self){
    serviceInfo.totalStatue = status;
    serviceInfo.dbJson.totalStatue = status ; //以k8s实际状态为准
    updateServiceStatus(clusterId,_username,serviceInfo.app ,status,self) ;
}

function updateServiceStatus(clusterId,username,app ,status ,self) {
    _udpAppStatus(self, username, app, status).then(function () {
        logger.info( "#updateServiceStatus: status revised." );
    }).catch(function (err) {
        logger.error("#updateServiceStatus:"+ JSON.stringify(err));
    });
}

/**
 * 从podItem中获取指定字段pod信息
 * Deprecated
 */
function getSimplifyPodInfo(item,_username ,clusterId){
    var podStatus = "error" ;
    switch (item.status.phase){
        case 'Pending':
            podStatus="pending" ;
            break;
        case 'Running':
            podStatus="running" ;
            break;
        case 'Succeeded':
            podStatus="stop" ;
            break;
        case 'Failed':
            podStatus="error" ;
            break;
        default:
            podStatus="error" ;
            logger.error("Unknow service status:" + item.status.phase  ) ;
    }
    var svc = {
        username:_username,
        clusterId: clusterId,
        pod:item.metadata.name, //pod={app}-v{ver}-hash
        app:item.metadata.labels["app"],
        ver:item.metadata.labels["version"],
        status: podStatus
    }
    return svc ;
}

libK8s.prototype.getAppServicesFromName = function (username, app) {
    return chkIsEndPointExist(this, username, app);
}

libK8s.prototype.updateAppService = function (username, svc, oldsvc) {
    var _self = this;
    var data = {
        app : svc.app,
        newsvc : svc,
        oldsvc : oldsvc
    }  //oops ,how big this object are !  sorry
    return withTransaction(username, data ,txManager.UPDATEAPP,closureMethod ) ;

    function closureMethod() {
        return new Promise(function (resolve, reject) {
            updateApp(_self, username, svc, oldsvc).then(function () {
                resolve(true);
            }).catch(function (err) {
                logger.error("can't updateApp..."+ JSON.stringify( err )  ) ;
                reject(err);
            });
        })
    }
}

libK8s.prototype.updateAppStatus = function (username, svc, status) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        _udpAppStatus(_self, username, svc, status).then(function () {
            resolve(true);
        }).catch(function (err) {
            reject(err);
        });
    })

}
libK8s.prototype.updateAutoStatus = function (username,svc,status) {
    var _self=this;
    return new Promise(function (resolve,reject) {
        _updateAutoStatus(_self,username,svc,status).then(function () {
            resolve(true);
        }).catch(function (err) {
            reject(err);
        })
    })
}
libK8s.prototype.updateScaleStatus = function (username,svc,data) {
    var _self=this;
    return new Promise(function (resolve,reject) {
        if(data.max<data.svc.dbJson.rcnum){
            data.svc.dbJson.rcnum=data.max;
        }
        _updateScaleStatus(_self,username,svc,data).then(function () {
            resolve(true);
        }).catch(function (err) {
            reject(err);
        })
    })
}

//compose part
libK8s.prototype.createCompose = function (ns, compInfo) {
    var _self = this;
    return withTransaction(ns,compInfo,txManager.CREATECOMP,closureMethod) ;

    function closureMethod(){
        return new Promise(function (resolve, reject) {
            var _rollback_stack = [];
            var _username = ns;
            var _data = compInfo;
            var promises = [];
            _data.services.forEach(function (one) {
                promises.push(_self.createComposeAppService(_username, one, _data.id));
            });
            Promise.all(promises).then(function (retlst) {

                var _err = '';
                for (var _t = 0; _t < retlst.length; ++_t) {
                    if (retlst[_t].ret != true) {
                        _err += retlst[_t].message + ';\n';
                    } else {
                        _rollback_stack.push({
                            fn: _self.deleteComposeAppService,
                            args: [_username, _data.services[_t], _data.id]
                        })
                    }
                }

                if (_err != '') {
                    throw new Error(_err);
                } else {
                    _data.username = _username;
                    delete _data.services;
                    compMetaInfoDB.insert(null, _data, function (err) {
                        if (err) {
                            throw new Error(err.message);
                        } else {
                            resolve(true);
                        }
                    });
                }
            })
                .catch(function (e) {
                    rollback(_self, _rollback_stack).then(function () {
                        reject(e);
                    }).catch(function (err) {
                        //rollback happened error
                        logger.error("createCompose rollback err:" + err);
                        reject( TxSupport().rollBackFailure(  e ) );
                    });

                });
        });
    }
}

libK8s.prototype.deleteCompose = function (ns, compInfo) {
    var _self = this;
    return withTransaction(ns,compInfo,txManager.DELETECOMP,closureMethod);

    function closureMethod(){
        return new Promise(function (resolve, reject) {
            var _rollback_stack = [];
            var _username = ns;
            var _data = compInfo;
            var promises = [];
            _data.services.forEach(function (one) {
                promises.push(_self.deleteComposeAppService(_username, one, _data.id));
            });

            Promise.all(promises).then(function (retlst) {
                var err = '';
                for (var _t = 0; _t < retlst.length; ++_t) {
                    if (retlst[_t].ret != true) {
                        err = retlst[_t].message;
                    } else {
                        _rollback_stack.push({
                            fn: _self.createComposeAppService,
                            args: [_username, _data.services[_t], _data.id]
                        })
                    }
                }

                if (err != '') {
                    throw new Error(err);
                } else {
                    compMetaInfoDB.delWithParam(null, {username: _username, id: _data.id}, function (err) {
                        if (err) {
                            throw new Error(err);
                        } else {
                            resolve(true);
                        }
                    });
                }
            }).catch(function (e) {
                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function (err) {
                    //rollback happened error
                    logger.error("deleteCompose rollback err:" + err);
                    reject(  TxSupport().rollBackFailure(  e )  );
                });
            });
        });
    }
}

libK8s.prototype.updateCompose = function (ns, compInfo) {
    var _self = this;
    return withTransaction(ns,compInfo,txManager.UPDATECOMP,closureMethod ) ;

    function closureMethod(){
        return new Promise(function (resolve, reject) {
            var _rollback_stack = [];
            var _username = ns;
            var _data = compInfo;
            var promises = [];
            var _addlst = _data.addservices;
            var _dellst = _data.delservices;
            _addlst.forEach(function (one) {
                promises.push(_self.createComposeAppService(_username, one, _data.id));
            });
            _dellst.forEach(function (one) {
                promises.push(_self.deleteComposeAppService(_username, one, _data.id));
            });
            var _all_svcs = _addlst.concat(_dellst);
            Promise.all(promises).then(function (retlst) {
                var err = '';
                for (var _t = 0; _t < retlst.length; ++_t) {
                    if (retlst[_t].ret != true) {
                        err = retlst[_t].message;
                    } else {
                        if (_t >= _addlst.length) {
                            _rollback_stack.push({
                                fn: _self.createComposeAppService,
                                args: [_username, _all_svcs[_t], _data.id]
                            })
                        } else {
                            _rollback_stack.push({
                                fn: _self.deleteComposeAppService,
                                args: [_username, _all_svcs[_t], _data.id]
                            })
                        }

                    }
                }

                if (err != '') {
                    throw new Error(err);
                } else {
                    resolve(true);
                }
            }).catch(function (e) {
                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function(err){
                    //rollback happened error
                    logger.error("updateCompose rollback err:"+err);
                    reject(   TxSupport().rollBackFailure(  e )   );
                });
            });
        });
    }
}

libK8s.prototype.createComposeAppService = function(username,svc,composeId){

    var _self = this;
    //default type is stack
    svc.type = 'stack';
    svc.composeId = composeId;
    var _ret = {
        ret:true
    };

    return new Promise(function (resolve, reject) {
        createApp(_self,username, svc).then(function () {
            resolve(_ret);
        }).catch(function (err) {
            _ret.ret = false;
            _ret.message = err.message;
            resolve(_ret);
        });
    });

}

libK8s.prototype.deleteComposeAppService = function (username, svc, composeId) {
    var _self = this;
    svc.composeId = composeId;
    var _ret = {
        ret: true
    };
    return new Promise(function (resolve, reject) {
        deleteApp(_self, username, svc).then(function () {
            resolve(_ret);
        }).catch(function (err) {
            _ret.ret = false;
            _ret.message = err.message;
            resolve(_ret);
        });
    })
}

//this method should not put here
libK8s.prototype.getServiceFromCompId = function (username, composeId) {
    return new Promise(function (resolve, reject) {
        svcMetaInfoDB.getWithParam(null, {username: username, composeId: composeId}, function (err, rets) {
            if (err) {
                reject(err.message);
            } else {
                resolve(rets);
            }
        });
    });
}
/**
 * get pods by label labelSelector
 * @param ns
 * @param cond
 */
libK8s.prototype.getPodsWithComposeName = function (ns, cond) {
    return getPods(this, ns, cond);
}

libK8s.prototype.getPodsByPodName = function (ns, podName) {
    if( podName === undefined || podName === ""){
        logger.warn("empty podName below ns:" +ns ) ;
        return {} ; //empty check
    }
    return getPodsByName(this, ns, podName) ;
}

libK8s.prototype.modifyReplicationController = function (ns, id, num, orgNum, svc,op) {
    var self = this ;
    return withTransaction(ns,svc,txManager.MODIFYRC,closureMethod ) ;

    function closureMethod(){
        return reSizeReplicas(self, ns, id, num, orgNum, svc,op);
    }
}

function grayUpdateStateful(_self, ns, svc, newversion) {
    delete svc._id;
    var username = ns;
    //copy old rc ,and set name and version
    var newsvc = _deepCopy(svc);
    newsvc.ver = newversion;
    var newRc = newsvc.petset;
    //newRc.metadata.labels.version = newversion;
    //newRc.spec.selector.matchLabels.version = newversion;
    newRc.spec.template.metadata.labels.version = newversion;
    var _last =  newRc.spec.template.spec.containers[0].image.lastIndexOf(':');
    newRc.spec.template.spec.containers[0].image = newRc.spec.template.spec.containers[0].image.substr(0,_last+1) + newversion;

    newsvc.node.image = newRc.spec.template.spec.containers[0].image;

    return updateStatefulApp(_self,username,newsvc,svc);
}

libK8s.prototype.grayUpdate = function (ns, svc, newversion) {
    var self = this ;
    return withTransaction(ns,svc,txManager.GRAYUPDATE,closureMethod ) ;
    //inner function
    function closureMethod() {
        if (svc.petset) {
            return grayUpdateStateful( self, ns, svc, newversion);
        }else{
            return grayUpdateStateless( self, ns, svc, newversion ) ;
        }
    }
    //update the stateless services
    function grayUpdateStateless( _self, ns, svc, newversion ){
        var username = ns;
        var composeId = svc.composeId;
        var replicas = svc.rcnum;
        //copy old rc ,and set name and version
        var newRc = _deepCopy(svc.rc);
        newRc.metadata.name = svc.app + '-v' + newversion;
        newRc.metadata.labels.version =   newversion;
        newRc.spec.template.metadata.labels.version = newversion;
        var _last = newRc.spec.template.spec.containers[0].image.lastIndexOf(':');
        newRc.spec.template.spec.containers[0].image = newRc.spec.template.spec.containers[0].image.substr(0, _last + 1) + newversion;

        return new Promise(function (resolve, reject) {

            var _rollback_stack = [];

            var _newId = newRc.metadata.name;
            newRc.spec.replicas = 0;
            createRc(_self, ns, newRc).then(function () {

                _rollback_stack.push({
                    fn: deleteRc,
                    args: [_self, username, newRc]
                });
                //此处会比较耗时,导致timeout返回给前端
                return reSizeRcForGrayUpt(_self, ns, svc.rc.metadata.name, newRc.metadata.name, replicas);
            }).then(function () {

                _rollback_stack.push({
                    fn: reSizeReplicas,
                    args: [_self, username, svc.rc.metadata.name, replicas]
                });

                return deleteRc(_self, ns, svc.rc);
            }).then(function () {
                _rollback_stack.splice(_rollback_stack.length - 1, 1, {
                    fn: createRc,
                    args: [_self, username, svc.rc]
                });

                return new Promise(function (_resolve, _reject) {
                    var cond = {};
                    if (composeId !== undefined)
                        cond = {composeId: composeId, app: svc.app};
                    else
                        cond = {username: username, app: svc.app};
                    //update rc object & svc objcect
                    newRc.spec.replicas = replicas;
                    svc.rc = newRc;
                    svc.ver = newversion;
                    svc.node.image = newRc.spec.template.spec.containers[0].image;
                    svc.totalStatue = "updating" ; //avoid previous status poisoning

                    //update db
                    svcMetaInfoDB.updateWithParam(null, cond, svc, function (err) {
                        if (err) {
                            logger.error("[grayUpdate]update svcMetaInfo failed,"+JSON.stringify(err)) ;
                            _reject(err);
                        } else {
                            _resolve(svc);
                        }
                    });
                });

            }).then(function (svc) {
                //all flow has down
                resolve(svc);
            }).catch(function (e) {
                logger.error("[grayUpdate]ocurrs exception when update,"+JSON.stringify(e)) ;
                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function (err) {
                    //rollback happened error
                    logger.info("grayUpdate rollback err:" + err);
                    reject(e);
                });

            });
        });
    }
}

/**
 * 检查该namespace下是否存在pod,并且返回该pod信息
 *  pod的状态有：
 *   Pending: The pod has been accepted , but at least one container image has not been created
     Running: The pod has been bound to a node, and all of the containers have been created.
     Succeeded: All containers in the pod have terminated in success, and will not be restarted.
     Failed: All containers in the pod have terminated .at least one container has terminated in failure
     Unknown: For some reason the state of the pod could not be obtained
 *  http://kubernetes.io/docs/user-guide/pod-states/
 */
libK8s.prototype.getAppPodStatus = function (ns, id) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        var url = 'namespaces/' + ns + '/pods';
        if (id)
            url += "?labelSelector=app=" + id;
        _self.kubeapi.get(url, function (err, data) {
            if (err) {
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}
/**
 * 根据label中的 app、version来select对应的rc对象，或者全部
 * "app": "ngx11",
 * "version": "1.9.14-alpine"
 * /api/v1/namespaces/hjqi6/replicationcontrollers?labelSelector=app=ngx11,version=1.9.14
 */
libK8s.prototype.getAppRC = function (ns, app,version) {
  var self = this ;
  return getRcFromK8s( self ,ns, app,version) ;
}

libK8s.prototype.getAppRCSimple = function (ns, app,version) {
    var self = this ;
    return  new Promise(function (resolve, reject) {
        //logger.debug( " getRcFromK8s processing ... " ) ;
        getRcFromK8s(self, ns, app, version).then(function (resp) {
            var rcList = [] ;
            if(resp.items){
                resp.items.forEach(function (rc) {
                    var required = rc.spec.replicas ;//用户创建的pod数目
                    var ready = rc.status.replicas ; //实际存货数目
                    version = version|| rc.metadata.labels.version ;
                    rcList.push({
                        "app":app,
                        "ver":version,
                        "required":required,
                        "ready":ready,
                        "image": rc.spec.template.spec.containers[0].image
                    }) ;
                })
            }
            resolve(rcList) ;
        }).catch(function ( err ) {
            logger.error("#getAppRCSimple happen exception.",err) ;
            reject(err) ;
        });
    });
}

function getRcFromK8s(self,ns, app,version){
    var _self = self;
    var url = 'namespaces/' + ns + '/replicationcontrollers?a=1&labelSelector=';
    var appStr = "" ;
    var verStr = "" ;

    return new Promise(function (resolve, reject) {
        if (app){
            appStr= "app=" + app;
        }
        if(version){
            verStr = "version=" + version ;
        }
        if(app){
            url+=appStr ;
        }
        if(version){
            if(app){
                url+=","+verStr ;
            }else{
                url+=verStr ;
            }
        }

        _self.kubeapi.get(url, function (err, data) {
            if (err) {
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}
libK8s.prototype.getAppPodLog = function (ns, podName ,sinceTime) {
    var _self = this;
    return new Promise(function (resolve, reject) {

        //kubeapi.get will format return string to json object so it need to user req by self
        //var _url = 'http://'+config.kubernetes.host+':'+config.kubernetes.port+'/api/v1/namespaces/'+ns+'/pods/'+ podName + "/log";
        var _url = '';
        if (sinceTime === undefined){
            _url = 'http://' + _self.host + ':' + _self.port + '/api/v1/namespaces/' + ns + '/pods/' + podName + "/log?tailLines=20";
        }else{
            var _url = 'http://' + _self.host + ':' + _self.port + '/api/v1/namespaces/' + ns + '/pods/' + podName + "/log?sinceTime=" + sinceTime;
        }
        _req._request(_url, 'GET', {}, '', function (data, result, response) {
            if (data !== null) {
                resolve(data.data);
            }
            else {
                reject(JSON.parse(result));
            }
        });
    });
}
/**
 * Get the events under a namespace with assigned type(Pod,ReplicationController,)
 * http://x.x.x.x/api/v1/namespaces/nature01/events?fieldSelector=involvedObject.kind=Pod,involvedObject.name=naruto2-v1.9.14-alpine-pfqw7
 */
libK8s.prototype.getAppPodEvents = function (ns, name,type) {
    var objName = "" ;
    if( !type ){
        type = 'Pod'; //default Pod
    }
    if( name ){
        objName = ",involvedObject.name="+name;
    }
    var _self = this;
    return new Promise(function (resolve, reject) {
        var _url = 'namespaces/' + ns
                + '/events?fieldSelector=involvedObject.kind='+type
                +objName;
        _self.kubeapi.get( _url , function (err, data) {
           // logger.debug("getAppPodEvents-> "+ JSON.stringify( data )) ;
           if(err !== null ){
               reject( JSON.stringify( err)  );
            }else{
               resolve( data );
            }
        });
    });
}


libK8s.prototype.getPersistentVolumeClaims = function (ns) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        _self.kubeapi.get('namespaces/' + ns + '/persistentvolumeclaims/pc0f46782ac98ff5782366b6-t-0', function (err, data) {
            if (err) {
                reject(err);
            }
            else if (data.code != undefined && data.kind == 'Status') {
                reject(data.message);
            } else
                resolve(data);
        });
    });
}
libK8s.prototype.getPersistentVolumeClaimByName = function (ns,name) {
    var _self = this;
    return new Promise(function (resolve, reject) {
        _self.kubeapi.get('namespaces/' + ns + '/persistentvolumeclaims/'+ name, function (err, data) {
            if (err) {
                reject(err);
            }
            else
                resolve(data);
        });
    });
}
libK8s.prototype.createPersistentVolume = function(ns,pv){
    var _self = this;
    if(pv.type === "hostpath"){
        return createLocalPathPV(_self,pv);
    }
    else if(pv.type === "glusterfs"){
        return createGlusterFsPV(_self,pv);
    }
    else if(pv.type === "ceph"){
        return createCephPV(_self,ns,pv);
    }
    else
        return emptyPromise();


}

libK8s.prototype.createPersistentVolumeClaims = function(ns,pvc){
    var _self = this;
    return new Promise(function(resolve, reject){
        _self.kubeapi.post('namespaces/'+ns+'/persistentvolumeclaims/',obj2pvc(pvc),function(err, data){
            if(err){
                reject(myRet(false,err.message));
            }
            else{
                if(data.code != undefined && data.code != 200){
                    reject(myRet(false,data.message));
                }else {
                    for(var i=0;i<3;i++){
                            _self.getPersistentVolumeClaimByName(ns,pvc.name).then(function (result) {
                                if(result.status.phase&&result.status.phase==='Bound'){
                                    return;
                                }else{
                                    setTimeout(function(){},2000);
                                }
                            });
                    }
                    resolve(myRet(true, undefined, data));
                }
            }
        });
    });
}

libK8s.prototype.deletePersistentVolume = function(ns,pvName){
    var _self = this;
    return new Promise(function(resolve, reject){
        //_self.kubeapi.delete('namespaces/'+ns+'/persistentvolumes/'+pvName,function(err, data){
        _self.kubeapi.delete('persistentvolumes/'+pvName,function(err, data){
            if(err){
                reject(err);
            }
            else{
                var result = data;
                if (typeof(data) === "string")
                    result = JSON.parse(data);
                if(result.code != undefined && result.code != 200){
                    //assert not found return succ when delete
                    if(result.reason === 'NotFound'){
                        resolve(result);
                    }else
                        reject(result);
                }else
                    resolve(result);
            }
        });
    });
}

libK8s.prototype.deletePersistentVolumeClaims = function(ns,pvcName){
    var _self = this;
    return new Promise(function(resolve, reject){
        _self.kubeapi.delete('namespaces/'+ns+'/persistentvolumeclaims/'+pvcName,function(err, data){
            if(err){
                reject(err);
            }
            else{
                var result = data;
                if (typeof(data) === "string")
                    result = JSON.parse(data);
                if(result.code != undefined && result.code != 200){
                    //assert not found return succ when delete
                    if(result.reason === 'NotFound'){
                        resolve(result);
                    }else
                        reject(result);
                }else
                    resolve(result);
            }
        });
    });
}

libK8s.prototype.createVolMgrSvc = function (ns, volume) {

    //init app service node
    var _svc = {};
    //app is namespace-volname
    //k8s allow service name at most 24 characters
    var _svcId = uuid(24, 16);
    _svc.app = _svcId;
    var _node = {};
    _node.ports = [];
    //init k8s service part
    var _p = {
        protocol: 'TCP',
        port: 80,
        targetPort: 80,
        accessProtocol: 'HTTP'
    };
    _node.ports.push(_p);

    //init k8s rc part
    _svc.ver = '1.0';
    _svc.rcnum = 1;
    _node.image = 'naturecloud.io/library/volume-explorer';
    /*_node.cpu_shares = '0.25';
     _node.mem_limit = '256Mi';*/
    _node.volumes = [];
    //add mount volume
    var _vol = {
        //this path should be config
        mountTargetPath: '/data',
        mountObj: {
            name: volume.name,
            volumeId: ns + '-' + volume.name
        },
        type:(volume.type ||"glusterfs")
    }
    _node.volumes.push(_vol);

    _svc.node = _node;

    return createAppForVolMgr(this, ns, _svc);
}

libK8s.prototype.createStorageSvcInOwnNS = function(ns,volumes){
    return _createStorageSvcInOwnNS(this,ns,volumes);
}

libK8s.prototype.createCephSecrectInOwnNS = function(ns){
    return _createCephSecret(this,ns);
}

libK8s.prototype.startJob = function (ns, volumeSvc) {

    var _self = this;
    return new Promise(function (resolve, reject) {

        getJobObj(ns, volumeSvc).then(function (job) {

            if (job !== undefined) {
                var _svc = {
                    app: volumeSvc,
                    job: job
                }
                return _createJob(_self, _svc.app, _svc.job);
            } else {
                //TODO
                //if create job again?
            }
        }).then(function (ret) {
            resolve(ret);
        }).catch(function (err) {
            reject(err);
        });
    });
}

libK8s.prototype.deleteJob = function (serviceId) {
    return _deleteJob(this, serviceId);
}

libK8s.prototype.createJob = function (serviceId, job) {
    return _createJob(this, serviceId, job);
}

libK8s.prototype.createSvcOnly = function (ns, svc) {
    return createSvc(this, ns, svc);
}

libK8s.prototype.deleteSvcOnly = function (ns, svc) {
    return deleteSvc(this, ns, svc);
}

libK8s.prototype.patchPullImageSecret = function (ns, secret) {
    return patchPullImageSecret(this, ns, secret);
}

function init(self, clusterInfo) {
    logger.info("k8s lib start to init [" + clusterInfo.name + "]...");
    self.id = clusterInfo.clusterId;
    self.host = clusterInfo.host;
    self.port = clusterInfo.port;
    self.IPs = clusterInfo.IPs.slice();
    self.kubeapi = K8s.api({
        endpoint: 'http://' + clusterInfo.host + ':' + clusterInfo.port,
        version: 'v1'
    });
    self.kubeapis_apps_v1alpha1 = K8s.api({
        endpoint:  'http://'+clusterInfo.host+':'+clusterInfo.port,
        basePath: 'apis',
        version: 'apps/v1alpha1'
    });
}

//if exist , also will return ture
function _createNameSpace(self, ns) {
    return new Promise(function (resolve, reject) {
        //var _url = 'http://'+config.kubernetes.host+':'+config.kubernetes.port+'/api/v1/namespaces';
        var _url = 'http://' + self.host + ':' + self.port + '/api/v1/namespaces';
        var namespaceInfo = {
            kind: "Namespace",
            apiVersion: "v1",
            metadata: {
                name: ns
            }
        }
        _req._request(_url, 'POST', {"Content-Type": "application/json"}, JSON.stringify(namespaceInfo), function (data, result, response) {
            if (data !== null) {
                resolve(data.data);
            }
            else {
                var _str = config.tunnel.domain+':'+config.tunnel.port;
                //if(result.indexOf('naturecloud.io:8080 not found') !== -1){
                if(result.indexOf(_str +' not found') !== -1){
                    reject({
                        message:'cluster is not ready, so not allow to operator it,please wait cluster ready'
                    });
                }else{
                    var _err = JSON.parse(result);
                    if (_err.reason === 'AlreadyExists'){
                        logger.info(ns+"[namespace] already exists.") ;
                        resolve(true);
                    }else{
                        logger.error(ns+"[namespace] can't be created."+_err.reason) ;
                        reject(_err);
                    }
                }
            }
        });
    });
}


function _deleteConfigMap(self, ns,app) {
    return new Promise(function (resolve, reject) {
        var _url = 'http://' + self.host + ':' + self.port + '/api/v1/namespaces/'+ns+'/configmaps?labelSelector=app='+app;
        _req._request(_url, 'DELETE', {"Content-Type": "application/json"}, "", function (data, result, response) {
            resolve(true);
        });
    });
}

function _createConfigMap(self, ns,cm) {
    return new Promise(function (resolve, reject) {
        var _url = 'http://' + self.host + ':' + self.port + '/api/v1/namespaces/'+ns+'/configmaps';
        _req._request(_url, 'POST', {"Content-Type": "application/json"}, JSON.stringify(cm), function (data, result, response) {
            var data=JSON.parse(result);
            if(data.kind=="ConfigMap")
                resolve(true);
            else
                reject(result);
        });
    });
}
function _createConfigMapAndVolume(self,ns,svc){
    if(svc.node.hasOwnProperty("config")){
        var files=null;
        var data=null;
        return configManager.implement(svc.node.imageshortname, svc.ver, ns, svc.node.config.name, svc.node.config.version)
        .then(function (_files) {
            files=_files;
            console.log(_files);
            return _deleteConfigMap(self,ns,svc.app);
        }).then(function () {
            data=configManager.getDef(files,svc.app);

            var querys=[];
            for(var index in data.configMaps){
                querys.push(_createConfigMap(self,ns,data.configMaps[index]));
            }
            svc.node.configmap=data;
            return Promise.all(querys);
        });
    }else
        return new Promise(function (resolve,reject) {
            resolve(true);
        })
}
function isStatefulApp(svc) {

    svc.node.volumes = svc.node.volumes || [];
    var isStateful = false;
    svc.node.volumes.forEach(function(volume) {
        isStateful = (isStateful||!volume.isShared);
        /**
         * don't know why set it
         */
        //volume.isStateful = !volume.isShared;
    });
    svc.node.isPetset = isStateful;
    return isStateful;
}

function createStatefulApp(_self,username,svc){
  if( isHostPathSvc(svc) ){
      //创建hostpath类型的有状态卷
      return new Promise(function (resolve, reject) {
          var _rollback_stack = [];
          var _id = svc.app;
          svc.username = username;
          svc.id = svc.app;

          svc.service = obj2Svc(svc, true);
          //default is starting status when app create
          svc.totalStatue = 'starting';
          var _node = svc.node;

          _createNameSpace(_self,username).then(function() {
              return _createPullImageSecret(_self, username, _node.secret);
          }).then(function(){
              if(svc.node.config.name){
                  return _createConfigMapAndVolume(_self,username,svc);
              } else {
                  return emptyPromise();
              }
          }).then(function () {
              return createVolumes(username,_id,_node.volumes, svc.rcnum)
          }).then(function () {
              return  formatVolumes(  username, svc.app, svc) ;
          }).then(function(){
              return createPVs(_self, username, _id, _node.volumes);
          }).then(function(){
              _rollback_stack.push({
                  fn:deletePVs,
                  args:[_self, username, _node.volumes]
              });
              return createPVCs(_self, username, _id, _node.volumes);
          }).then(function(){
              _rollback_stack.push({
                  fn:deletePVCs,
                  args:[_self, username, _node.volumes]
              });
              return createSvc(_self,username, svc.service)
          }).then(function(svcResponse){
              updatePublicAddress(_self, username,svcResponse, svc);
              svc.petset = obj2PetSet(svc);
              _rollback_stack.push({
                  fn:deleteSvc,
                  args:[_self,username,_id]
              });
              logger.debug( "debug ----checkpoint 1" ) ;
              return createPetset(_self,username, svc.petset);
          }).then(function () { //update service metainfo
              _rollback_stack.push({
                  fn: deletePetset,
                  args: [_self, username, svc.app]
              });
              //return updateVolumesStatus(svc, true);
              logger.debug( "debug ----checkpoint 2" ) ;
              return insertHostPathInfo( svc ) ;
          }).then(function(){
              logger.debug( "debug ----checkpoint 3" ) ;
              return new Promise(function (_resolve, _reject) {
                  mongosvcformat(svc);
                  var dbSvc = proInvalidMKeyInPetSet(svc);
                  svcMetaInfoDB.insert(null, dbSvc, function (err) {
                      if (err) {
                          _reject(err);
                      } else {
                          _resolve(true);
                      }
                  });
              });
          }).then(function(){
              //all flow finish
              resolve(true);
          }).catch(function (e) {

              rollback(_self,_rollback_stack).then(function(){
                  reject(e);
              }).catch(function(err){
                  //rollback happened error
                  logger.error("createStatefulApp rollback err:"+err);
                  reject( TxSupport().rollBackFailure( e ) );
              });
          });
      });
  }else{
    return new Promise(function (resolve, reject) {
        var _rollback_stack = [];

        var _id = svc.app;
        svc.username = username;
        svc.id = svc.app;

        svc.service = obj2Svc(svc, true);
        //default is starting status when app create
        svc.totalStatue = 'starting';
        var _node = svc.node;

        _createNameSpace(_self,username).then(function() {
            return _createStorageSvcInOwnNS(_self,username,_node.volumes);
        }).then(function(){
            if(svc.node.config.name){
                return _createConfigMapAndVolume(_self,username,svc);
            } else {
                return emptyPromise();
            }
        }).then(function () {
            return createVolumes(username,_id,_node.volumes, svc.rcnum)
        }).then(function(){
            _rollback_stack.push({
                fn:deleteVolumes,
                args:[username,_id,_node, svc.rcnum]
            });
            return createPVs(_self, username, _id, _node.volumes);
        }).then(function(){
            _rollback_stack.push({
                fn:deletePVs,
                args:[_self, username, _node.volumes]
            });
            return createPVCs(_self, username, _id, _node.volumes);
        }).then(function(){
            _rollback_stack.push({
                fn:deletePVCs,
                args:[_self, username, _node.volumes]
            });
            return createSvc(_self,username, svc.service)
        }).then(function(svcResponse){
            _rollback_stack.push({
                fn:deleteSvc,
                args:[_self,username,_id]
            });
            updatePublicAddress(_self, username,svcResponse, svc);
            svc.petset = obj2PetSet(svc);

            return createPetset(_self,username, svc.petset);
        }).then(function () { //update service metainfo
            _rollback_stack.push({
                fn: deletePetset,
                args: [_self, username, svc.app]
            });
            return updateVolumesStatus(svc, true);
        }).then(function(){
            return new Promise(function (_resolve, _reject) {
                mongosvcformat(svc);
                var dbSvc = proInvalidMKeyInPetSet(svc);
                svcMetaInfoDB.insert(null, dbSvc, function (err) {
                    if (err) {
                        _reject(err);
                    } else {
                        _resolve(true);
                    }
                });
            });
        }).then(function(){
            //all flow finish
            resolve(true);
        }).catch(function (e) {

            rollback(_self,_rollback_stack).then(function(){
                reject(e);
            }).catch(function(err){
                //rollback happened error
                logger.error("createStatefulApp rollback err:"+err);
                reject( TxSupport().rollBackFailure( e ) );
            });
        });
    });
  }
}
function createStatefulVolume(ns, app, volObj, replicaNum, start,statefulPolicy){
    var promises = [];
    var startValue = start ? start : 0;
    var volNamePrefix = "";
    var isSpecVolume = true;
    /**
     *  mount specially volume ,so mountObj prop is not {}
     *  will discard  mountArray and mountObj
     */

    if(!volObj.IsDefault){
        volNamePrefix = volObj.mountName;
    }
    else if(volObj.mountName !== undefined){
        isSpecVolume = false;
        volNamePrefix = volObj.mountName;
    }else if(volObj.mountObj){
        volObj.mountObj.mountName = volObj.mountObj.name;
        volNamePrefix = volObj.mountObj.name;
    }
    else{
        isSpecVolume = false;
        volNamePrefix = uuid(24, 16);
    }

    //default policy is reuse
    var _policy = statefulPolicy ? statefulPolicy:Common.PVPolicy.REUSE;
    for (var i = startValue; i < replicaNum; i++) {
        var vol = "";
        if(isSpecVolume && i === 0)
            vol = volNamePrefix;
        else{
            vol = volNamePrefix + "-" + app + "-"+i;
        }
        var _create_volume;
        if(volObj.size){
            _create_volume = {
                mountName:volNamePrefix,
                volumeName:vol,
                volumeSize:volObj.size
            }
        } else {
            _create_volume = {
                mountName:volNamePrefix,
                volumeName:vol
            }
        }

        if(volObj.type==='hostpath'){
            var tmp=volObj.volumeId.split('-')[0];
            var newvol={};
            newvol.volumeId =tmp+'-'+i;
            newvol.mountName=volObj.mountName;
            newvol.name=volObj.name+'-'+app+'-'+i;
            newvol.type='hostpath';
            var volume={};
            volume.data=newvol;
            volume.ret=true;
            promises.push(volume);
        }else {
            promises.push(_storageHandle.createVolume(ns, _create_volume, _policy));
        }
    }
    return Promise.all(promises);
}

function deleteVolumeForce(ns, app, encodeMountPath, replicaNum){
    var promises = [];
    for (var i = 0; i < replicaNum; i++) {
        var vol = app + "-" + i + "-" + encodeMountPath;
        promises.push(_storageHandle.deleteVolumeForce(ns, vol));
    }
    return Promise.all(promises);
}

function deleteStatefulVolumeByScale(ns, app, encodeMountPath, replicaNum,start){
    var promises = [];
    var startValue = start ? start : 0;
    for (var i = startValue; i < replicaNum; i++) {
        var vol = ns+"-"+ app + "-" + i + encodeMountPath;
        //for rollback call,so use prototype instand this
        promises.push(_storageHandle.deleteVolumeForce(ns, vol));
    }
    return Promise.all(promises);
}

function deleteVolumeBySvc(service){

    var volumeIdList = [];
    service.node.volumes.forEach(function(volume){
        /*if(!volume.isShared){
            volume.mountArray.forEach(function(_vol){
                volumeIdList.push(_vol.volumeId)
            });

        }else
            volumeIdList.push(volume.mountObj.volumeId);*/
        volumeIdList.push(volume.volumeId);
    });

    logger.info("need to delete volume list is:"+volumeIdList.join(","));
    deleteVolumeTest();
    function deleteVolumeTest() {
        storageInfoDB.getWithParam(null, {namespace:service.username, volumeType:{ $exists:false},volumeId:{$in:volumeIdList}},function(err,rets){
            if(err)
                logger.error("app [%s] get volumes error when delete app",service.app);
            else{
                //logger.info("after db filter, delete volume list is:"+rets.join(","));
                rets.forEach(function(volume) {
                    //volume not share with other service
                    if (volume.mountAppNameArray === undefined || volume.mountAppNameArray.length === 0) {
                        _storageHandle.deleteVolumeForce(volume.namespace, volume.volumeId).then(function (result) {
                            if (result.ret === true) {
                                logger.info("delete volume [%s] successful", volume.volumeId);
                            } else {

                                logger.error("delete volume [%s] failed [%s]", volume.volumeId, result.data);
                            }
                        }).catch(function (err) {
                            if (err instanceof Error) {
                                logger.error("delete volume [%s] error [%s]", volume.volumeId, err.message);
                            } else {

                                setTimeout(function () {
                                    deleteVolumeTest();
                                },500)
                                logger.error("delete volume [%s] error", volume.volumeId);
                            }
                        });
                    }
                });
            }

        });
    }

}
function createPVs(_self, ns, app, volumes,start) {
    var promises = [];
    var _start = start?start:0;
    var ids=[];
    volumes.forEach(function (volume) {
        logger.debug("++++++++"+volume.size);
        if(!volume.pv){
            //预警，此处有hard code
            if( volume.type === "hostpath"&& !volume.isShared){
                var id='';
                if(volume.volumeId.split('-')[2]){
                    id=volume.volumeId.split('-')[1]+(volume.volumeId.split('-')[2]);
                }else{
                    id=volume.volumeId.split('-')[0]+'-'+(volume.volumeId.split('-')[1]);
                }
                if(ids.indexOf(id)<0) {
                    ids.push(id);
                    var pvName = volume.name;
                    if (volume.IsDefault) {
                        pvName = pvName + "-" + app + "-" + 0;
                    }
                    if (!volume.size) {
                        volume.size = 1000;
                    }
                    //var pvName = volume.mountObj.name+"-"+app+"-"+0;
                    var pv = {
                        name: pvName,
                        size: volume.size,
                        path: volume.volumeId,
                        isShare: volume.isShared
                    };

                    pv.type = volume.type;
                    volume.pv = pvName;
                    //if(volume.IsDefault){
                    //    pv.name=pvName+"-"+app+"-"+0;
                    //}
                    promises.push(_self.createPersistentVolume(ns, pv));
                }
        }else if (!volume.isShared) {

                /**
                 *  如果是指定挂载，isdefault为false,则pv name需要规范为
                 *   xxx - app - 0
                 */

                if(volume.IsDefault === false){
                    volume.pv = volume.name + "-" + app + "-0";
                }else
                    volume.pv = volume.name;

                var pv = {
                    name: volume.pv,
                    size: volume.size,
                    path: volume.volumeId,
                    isShare:volume.isShared
                };
                if(pv.path === undefined)
                    console.log(pv);

                pv.type = volume.type || "glusterfs"; //  hostpath or glusterfs or ceph
                promises.push(_self.createPersistentVolume(ns, pv));

            }
        }


    });
    return Promise.all(promises);
}

function createPVCs(_self, ns, app, volumes,start) {
    var promises = [];
    var _start = start?start:0;
    volumes.forEach(function(volume) {

        if(!volume.pvc){
            if( volume.type === "hostpath"&& !volume.isShared ){
                //hard code tips, please change this with refactoring
                //var pvcName = genPVCName(volume.mountTargetPath, app, 0);
                var pvcName = volume.pv;
                var pvc = {
                    name: pvcName,
                    size: volume.size,
                   isShare:volume.isShared
                }
                volume.pvc = pvcName;
                promises.push(_self.createPersistentVolumeClaims(ns, pvc));
            }else if (!volume.isShared) {

                var pvc = {
                    name: volume.pv,
                    size: volume.size,
                    isShare:volume.isShared
                }
                //volume.mountName = pvc.name;
                volume.pvc = pvc.name;
                promises.push(_self.createPersistentVolumeClaims(ns, pvc));

            }
        }
    });
    return Promise.all(promises);
}

function deletePVs(_self, ns, volumes) {
    var promises = [];
    volumes.forEach(function (volume) {
        if( volume.type === "hostpath" ){
            //hard code tips, please change this with refactoring
            var pvcName = volume.pv //genPVCName(volume.mountTargetPath, app, 0);
            promises.push( _self.deletePersistentVolume(ns, pvcName ) );
        } else if (!volume.isShared) {
            promises.push(_self.deletePersistentVolume(ns, volume.pv));
        }
    });
    return Promise.all(promises);
}

function deletePVCs(_self, ns, volumes) {
    var promises = [];
    //var volumes = node.volumes;
    volumes.forEach(function(volume) {
        if( volume.type === "hostpath" ){
            //hard code tips, please change this with refactoring
            var pvcName = volume.pvc ;
            promises.push( _self.deletePersistentVolumeClaims(ns, pvcName ));
        }else if (!volume.isShared) {
            promises.push(_self.deletePersistentVolumeClaims(ns, volume.pvc));
        }
    });
    return Promise.all(promises);
}

function deletePVsScaleDown(_self, ns, node,down) {
    var promises = [];
    var volumes = node.volumes;
    //var isPetset = node.isPetset;
    volumes.forEach(function (volume) {
        if (!volume.isShared) {
            var _index = getVolumeIndex(volume);
            if(_index >= down)
                promises.push(_self.deletePersistentVolume(ns, volume.pv));
            //}
        }
    });
    return Promise.all(promises);
}

function deletePVCsScaleDown(_self, ns, node,down) {
    var promises = [];
    var volumes = node.volumes;
    //var isPetset = node.isPetset;
    volumes.forEach(function(volume) {
        if (!volume.isShared) {
            var _index = getVolumeIndex(volume);
            if(_index >= down)
                promises.push(_self.deletePersistentVolumeClaims(ns, volume.pvc));
        }
    });
    return Promise.all(promises);
}

function getVolumeIndex(volume){
    var _idx = volume.name.lastIndexOf('-');
    var _index = volume.name.substr(_idx+1);
    return parseInt(_index);
}

function createGlusterFsPV(k8s,pv){
    return reqPersistentVolumes(k8s,obj2GlusterfsPv(pv));
}

function createCephPV(k8s,ns,pv){
    return reqPersistentVolumes(k8s,obj2CephPv(ns,pv));
}

function createLocalPathPV(k8s,pv){
    return reqPersistentVolumes(k8s,obj2LocalPathPv(pv));
}

function reqPersistentVolumes(k8s,data){
    return new Promise(function(resolve, reject){
        k8s.kubeapi.post('persistentvolumes/',data,function(err, resp){
            if(err){
                logger.error("can not create  pv for "+JSON.stringify( err ) ) ;
                reject(myRet(false,err.message));
            }
            else{
                if(resp.code != undefined && resp.code != 200){
                    reject(myRet(false,resp.message));
                }else
                    resolve(myRet(true,undefined,resp));
            }
        });
    });
}

function proInvalidMKeyInPetSet(svc) {
    var newSvc = deepcopy(svc);
    delete newSvc.petset.spec.template.metadata.annotations["pod.alpha.kubernetes.io/initialized"]
    return newSvc;
}

//it will parser more in future
function obj2PetSet(svc){
    var itemId = svc.app;
    var obj = svc.node;
    var _version = svc.ver;

    var petsetName = itemId+(_version == ''?'':'-v'+_version);
    var labels =  {
        app: itemId,
        version: _version
    };
    var metadata = {
        labels: labels,
        annotations: {
            "pod.alpha.kubernetes.io/initialized": "true"
        }
    };
    var ports = [];
    for(var j=0;j< obj.ports.length; j++){
        ports.push({
            "containerPort": parseInt(obj.ports[j].port),
            "protocol": obj.ports[j].protocol
        });
    }
    //set volume
    var volumeMounts = [];
    var volumeClaimTemplates = [];
    var volumesMetaArray = [];
    if(obj.volumes !== undefined){
        //record volume
        var _volumeMap = {};

        //set container part
        for(var _i=0;_i< obj.volumes.length; _i++){

            //scaleable volume not do
            if(obj.volumes[_i].IsDefault === undefined)
                continue;

            var mountName ="";
            /**
             *   assert that 2 cases:
             *   1.如果没有pv 就是共享卷 mountName 取name
             *   2.有pv的是 stateful volume
             *
             */
            if(obj.volumes[_i].pv){
                var _idx = obj.volumes[_i].pv.indexOf('-'+svc.app);
                if(_idx > -1)
                    mountName = obj.volumes[_i].pv.substr(0,_idx);
                else
                    mountName = obj.volumes[_i].pv;
            }else{
                mountName = obj.volumes[_i].name;
            }


            obj.volumes[_i].mountName = mountName;
            volumeMounts.push({
                "mountPath":obj.volumes[_i].mountTargetPath,
                "name":mountName
            });

            //put volume in list
            if(!_volumeMap.hasOwnProperty(mountName))
                _volumeMap[mountName] = obj.volumes[_i];
        }

        /**
         *   stateful volume create volume claim tpl
         *   default volume push template.spec.volumes array
         *
         *   if volume change
         */
        for(var name in _volumeMap){
            var _vol = _volumeMap[name];
            if(!_vol.isShared || isHostPathSvc(svc ) ) {
                if (_vol.isShared) {
                    volumesMetaArray.push({
                        "name": name,
                        "hostPath": {
                            "path": _vol.volumeId
                        }
                    });
                } else {
                    volumeClaimTemplates.push({
                        metadata: {
                            "name": name
                        },
                        spec: {
                            accessModes: ["ReadWriteOnce"],
                            resources: {
                                requests: {
                                    storage: "1Gi"
                                }
                            }
                        }
                    });
                }
            }
            else{

                if(_vol.type === "ceph"){
                    volumesMetaArray.push({
                        "name": name,
                        "rbd": {
                            "monitors": config.cephMeta.monitors,
                            "pool": svc.username,
                            "image": _vol.volumeId,
                            "fsType": "ext4",
                            "readOnly": true
                        }
                    });
                }else{
                    volumesMetaArray.push({
                        "name": name,
                        "glusterfs": {
                            "endpoints": "glusterfs-cluster",
                            "path": _vol.volumeId
                        }
                    });
                }
            }


        }
    }

    // Set containers
    var container0 = {
        name:itemId,
        image:obj.image,
        imagePullPolicy:"Always",
        env: obj.env,
        resources:{
            limits: {
                cpu: (obj.cpu_shares*1000).toString()+"m",
                memory: obj.mem_limit
            }
        },
        ports: ports,
        volumeMounts: volumeMounts
    }
    if (obj.commands) {
        container0.command = obj.commands.split(" ");
    }
    //set command
    if(obj.commands !== undefined && _commandMap.hasOwnProperty(obj.commands)){
        container0.command = _commandMap[obj.commands].slice();
    }
    //set args
    if(obj.args != undefined ){
        container0.args = obj.args.slice();
    }
    var containers =[];
    containers.push(container0);

    var petset = {
        apiVersion: "apps/v1alpha1",
        kind: "PetSet",
        metadata: {
            name: itemId,
            labels: labels
        },
        spec: {
            serviceName: itemId,
            replicas: parseInt(svc.rcnum),
            template: {
                metadata: metadata,
                spec: {
                    terminationGracePeriodSeconds: config.kubernetes.terminationGracePeriodSeconds,
                    restartPolicy: obj.restart,
                    imagePullSecrets:[{"name":"pullsecret"}],
                    containers: containers
                }
            },
            volumeClaimTemplates: volumeClaimTemplates
        }
    };
    petset.spec.template.spec.nodeSelector={};
    if( isHostPathSvc(svc) ){
        petset.spec.template.spec.nodeSelector.highIO=config.highIO;
    }
    if(config.network){
       // petset.spec.template.metadata.annotations={};
        petset.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity']='';
        var affinity={}
        affinity.nodeAffinity={};
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution={};
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms=[];
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]={};

        var matchExpressions=[];
        matchExpressions[0]={};
        matchExpressions[0].key='network';
        if(obj.isNetwork) {
            matchExpressions[0].operator = 'In';
        }else{
            matchExpressions[0].operator = 'NotIn';
        }
        matchExpressions[0].values=[];
        matchExpressions[0].values[0]=config.network;
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions=matchExpressions;
        petset.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity']=JSON.stringify(affinity);
    }

    if(volumesMetaArray.length > 0)
        petset.spec.template.spec.volumes = volumesMetaArray.slice();
    logger.debug( "petset "+JSON.stringify(petset) ) ;
    if(obj.hasOwnProperty("configmap")) {
        petset.spec.template.spec.volumes = petset.spec.template.spec.volumes || [];
        petset.spec.template.spec.volumes = petset.spec.template.spec.volumes.concat(obj.configmap.volumes);
        petset.spec.template.spec.containers[0].volumeMounts = petset.spec.template.spec.containers[0].volumeMounts.concat(obj.configmap.volumeMounts);
    }
    return petset;
}

function createPetset(self,ns,petset){

    return new Promise(function(resolve, reject){
         self.kubeapis_apps_v1alpha1.post('namespaces/'+ns+'/petsets',petset,function(err, data){
            if(err){
                logger.error( "Create petset error:"+JSON.stringify( err)  ) ;
                reject(err);
            }
            else if(data.code != undefined && data.kind == 'Status'){
                //ft for create rc
                if(data.reason === 'AlreadyExists')
                    resolve();
                else
                    reject(data.message);
            }else
                resolve(data);
        });
    });
}

function deletePetset(self,ns,id){
    return new Promise(function(resolve, reject){
        self.kubeapis_apps_v1alpha1.delete('namespaces/'+ns+'/petsets/'+id,function(err, data){
            if(err){
                reject(err);
            }
            else{
                resolve(data);
            }
        });
    });
}
/**
 * create rc and service in k8s
 * @returns {Promise}
 */
function createApp(_self, username, svc) {

    return withTransaction(username,svc,txManager.CREATEAPP,closureProcedure) ;



    function closureProcedure(){
        setSvcFieldIntoNode(svc,username);
        var isStateful = isStatefulApp(svc);
        //debug  set stateful true
        if (isStateful){
            return createStatefulApp(_self,username,svc);
        }else{
            return createStatelessApp( _self, username, svc ) ;
        }
    }
}

function createStatelessApp( _self, username, svc ){
    return new Promise(function (resolve, reject) {
        var _rollback_stack = [];
        var _id = svc.app;
        svc.username = username;
        svc.id = svc.app;
        svc.service = obj2Svc(svc);
        svc.totalStatue = 'starting';
        var _node = svc.node;

        if ( !isHostPathSvc(svc) ) {

            _createNameSpace(_self, username).then(function(){
                //step 1 create endpoint & service
                return _createStorageSvcInOwnNS(_self, username, _node.volumes)
            }).then(function(){
                if(svc.node.config.name){
                    return _createConfigMapAndVolume(_self,username,svc);
                } else {
                    return emptyPromise();
                }
            }).then(function () {
                //step 2 create volume in gluster fs
                return createVolumes(username, _id, _node.volumes)
            }).then(function () {
                //create serect for image pull
                return _createPullImageSecret(_self,username,_node.secret);
            }).then(function () {
                //logger.debug( ">>>>>>>createsvc ") ;
                _rollback_stack.push({
                    fn: deleteVolumes,
                    args: [username, _id, _node.volumes]
                });
                //create service for port map(contain port : host port)
                return createSvc(_self, username, svc.service)
            }).then(function (svcResponse) {
                updatePublicAddress(_self,username,svcResponse, svc);
                svc.rc = obj2Rc(svc,_self); // construct a rc json parameter object
                _rollback_stack.push({
                    fn: deleteSvc,
                    args: [_self, username, _id]
                });
                //create a replication controller
                return createRc(_self, username, svc.rc);
            }).then(function () {
                //update service metainfo

                _rollback_stack.push({
                    fn: deleteRc,
                    args: [_self, username, svc.rc,0]
                });

                return updateVolumesStatus(svc, true);
            })
            .then(function () {
                return new Promise(function (_resolve, _reject) {
                    mongosvcformat(svc);
                    svcMetaInfoDB.insert(null, svc, function (err) {
                        if (err) {
                            _reject(err);
                        } else {
                            _resolve(true);
                        }
                    });
                });
            })
            .then(function () {
                //all flow finish
                resolve(true);
            }).catch(function (e) {

                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function (err) {
                    //rollback happened error
                    logger.info("createApp rollback err:" + err);
                    reject( TxSupport().rollBackFailure( e ) );
                });
            });
        } else {
            //自由主机
            // 创建namespace \ rc \数据库记录
            //step 1 create  namespace
            formatVolumes(username, _id, svc) ;
            _createNameSpace(_self, username)
            .then(function(){
                return _createPullImageSecret(_self,username,_node.secret);
            })
            .then(function(){
                if(svc.node.config.name){
                    return _createConfigMapAndVolume(_self,username,svc);
                } else {
                    return emptyPromise();
                }
            }).then(function () {
                //端口访问映射(tcp or udp)
                return createSvc(_self, username, svc.service)
            }) .then(function (ret) {
                var svcResponse = ret;
                //roll_back must be put front of logic
                _rollback_stack.push({
                    fn: deleteSvc,
                    args: [_self, username, _id]
                });
                updatePublicAddress(_self,username,svcResponse, svc);
                svc.rc = obj2Rc(svc,_self); // construct a rc json parameter object
                return createRc(_self, username, svc.rc);
            })  .then(function () {
                _rollback_stack.push({
                    fn: deleteRc,
                    args: [_self, username, svc.rc,0]
                });
                return insertHostPathInfo(svc);
            }).then(function () {
                    return new Promise(function (_resolve, _reject) {
                        mongosvcformat(svc);
                        //将这个svc对象整个的存入数据库中
                        svcMetaInfoDB.insert(null, svc, function (err) {
                            if (err) {
                                _reject(err);
                            } else {
                                _resolve(true);
                            }
                        });
                    });
            }).then(function(){
                resolve(true);
            }).catch(function (e) {
                logger.error("can not create container "+ JSON.stringify( e )) ;
                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function (err) {
                    logger.info("createApp[type:free host] rollback err:" + err);
                    reject( TxSupport().rollBackFailure( e ) );
                });
            });
        }

    });
}

/**
 *  will use node object , and set service object info into node object
 * @param svc
 */
function setSvcFieldIntoNode(svc,username){
    svc.node.replicas= svc.rcnum;
    svc.node.version = svc.ver;
}

//for volume manager service
function createAppForVolMgr(_self, username, svc) {
    //var _self = this;
    return new Promise(function (resolve, reject) {
        var _rollback_stack = [];
        var _volumeId = svc.node.volumes[0].mountObj.volumeId;
        var _id = svc.app;
        svc.username = username;
        svc.id = svc.app;

        svc.service = obj2Svc(svc);

        var _node = svc.node;

        //storage service delete only when user account delete from system,so no need rollback
        //create when system init
        //createStorageSvcInOwnNS(username,[true])
        //    .then(function(){
        //        return createSvc(username, svc.service);
        //    })
        createSvc(_self, 'default', svc.service)
            .then(function (svcResponse) {
                //svc.rc = obj2Rc(svc);
                svc.job = obj2Job(svc);
                _rollback_stack.push({
                    fn: deleteSvc,
                    args: [_self, username, _id]
                });

                //return createRc(username, svc.rc);
                return _createJob(_self, svc.app, svc.job);
            }).then(function () {

            _rollback_stack.push({
                fn: _deleteJob,
                args: [_self, svc.app]
            });

            return new Promise(function (_resolve, _reject) {

                storageInfoDB.updateField(null, {namespace: username, volumeId: _volumeId}, {
                    svcId: svc.app,
                    job: svc.job,
                    svc: svc.service
                }, function (err) {
                    if (err) {
                        _reject(err);
                    } else {
                        _resolve(svc.app);
                    }
                });
            });


        }).then(function (svcId) {
            //all flow finish
            resolve({
                ret: true,
                svcId: svcId
            });
        }).catch(function (e) {

            rollback(_self, _rollback_stack).then(function () {
                reject(e);
            }).catch(function (err) {
                //rollback happened error
                logger.info("createApp rollback err:" + err);
                reject(e);
            });

        });
    });
}

// 会改变svc.node.ports参数里的具体值
//dig a hole and do not tell anyone
function updatePublicAddress(self,ns,svcResponse, svc) {
    logger.debug(ns+":"+svc.app+"updateApp updatePublicAddress");
    if( !svcResponse.data ){
        //change or not change,i must be a nerd
        logger.error("No response existed",svcResponse.error ) ;
    }
    svcResponse = svcResponse.data||svcResponse ;
    var responsePorts = svcResponse.spec.ports;
    var originalPorts = svc.node.ports;
    for (var i = 0; i < originalPorts.length; i++) {
        var originalPort = originalPorts[i];
        var responsePort = responsePorts[i];
        if (originalPort.isPublic) {
            if(self.id === 'default'){
                switch (originalPort.accessProtocol) {
                    case "HTTP":
                        originalPort.address = 'http://' + svc.app + '.' + ns + '.' + config.domainName;
                        break;
                    case "TCP":
                        originalPort.address = config.externalIP + ":" + responsePort.nodePort;
                        originalPort.nodePort = responsePort.nodePort;
                        break;
                    default:
                        break;
                }
            }
            //self cluster mode
            else{
                originalPort.address = "";
                var _addrs = [];
                self.IPs.forEach(function(ip){
                    if(ip !== undefined && ip !== null){
                        _addrs.push(ip+":"+ responsePort.nodePort);
                    }
                });
                originalPort.address = _addrs.join(',');
                originalPort.nodePort = responsePort.nodePort;
            }
        }
    }
}
/**
 * - set rc replicas to 0
 * - delete rc
 * - delete svc
 * - update volume status to "Not used"
 * - delete serviceInfo in database
 * TODO 删除服务应该根据app 和版本
 * @param   {Boolean} isDelVolume   true: delete volume  ;  false,undefined: not to deal with volume
 */
function deleteApp(_self, username, svc,isDelVolume) {
    return withTransaction(  username, svc,txManager.DELETEAPP ,closureMethod ) ;

    function closureMethod() {
        if (svc.petset){
            return deleteStatefulApp(_self, username, svc,isDelVolume);
        }else{
            return deleteStateLessApp(_self, username, svc,isDelVolume) ;
        }
    }
}

function deleteStatefulApp(_self,username,svc,isDelVolume){
    //var _self = this;
    return new Promise(function (resolve, reject){

        var _rollback_stack = [];

        deletePetset(_self,username,svc.app).then(function(){

            var newSvc = addInvalidMKeyIntoPetset(svc);
            _rollback_stack.push({
                fn:createPetset,
                args:[_self,username, newSvc.petset]
            });

            return deletePods(_self,username,"app=" + svc.app);
        }).then(function() {
            return deletePVCs(_self,username, svc.node.volumes)
        }).then(function() {
            _rollback_stack.push({
                fn:createPVCs,
                args:[_self, username, svc.app, svc.node.volumes]
            });
            return deletePVs(_self,username, svc.node.volumes);
        }).then(function(){
            _rollback_stack.push({
                fn:createPVs,
                args:[_self, username, svc.app, svc.node.volumes]
            });
            return deleteSvc(_self,username,svc.app);
        }).then(function(){

            _rollback_stack.push({
                fn:createSvc,
                args:[_self,username,svc.service]
            });

            return updateVolumesStatus(svc,false);

        }).then(function(){
            return new Promise(function (_resolve, _reject){
                var cond = {};
                if(svc.composeId !== undefined)
                    cond = {composeId:svc.composeId,app:svc.app};
                else
                    cond = {username:username,app:svc.app};
                svcMetaInfoDB.delWithParam(null,cond,function(err){
                    if(err){
                        _reject(err);
                    }else{
                        _resolve(true);
                    }
                });
            });
        }).then(function(){
            //all finish
            new Promise(function(res,rej){
                svc.username = username ;
                clearHomeLessHostPathRecord( svc ) ;

                //delete volume
                if(isDelVolume)
                    deleteVolumeBySvc(svc); //notice ,this can not delete the hostpath volume because volume type is specified to default

                res(true ) ;
            }).catch( function(e){
                res(true ) ;
            });
            resolve(true);
        }).catch(function (e) {
            logger.info("deleteStatefulApp err:", e);
            rollback(_self,_rollback_stack).then(function(){
                reject(JSON.parse(e));
            }).catch(function(err){
                //rollback happened error
                logger.info("deleteStatefulApp rollback err:"+err);
                var rejectResult = e;
                if (typeof(e) === "string")
                    rejectResult = JSON.parse(e);
                reject( TxSupport().rollBackFailure( rejectResult ) );
            });
        });
    });
}

function deleteStateLessApp(_self, username, svc,isDelVolume) {
    return new Promise(function (resolve, reject) {

        var _rollback_stack = [];

        reSizeReplicas(_self, username, svc.app + '-v' + svc.ver, 0,undefined,undefined,'delete').then(function () {

            _rollback_stack.push({
                fn: reSizeReplicas,
                args: [_self, username, svc.app + '-v' + svc.ver, svc.rcnum]
            });

            return deleteRc(_self, username, svc.rc);
        }).then(function () {

            _rollback_stack.splice(_rollback_stack.length - 1, 1, {
                fn: createRc,
                args: [_self, username, svc.rc]
            });

            return deleteSvc(_self, username, svc.app);
        }).then(function () {

            _rollback_stack.push({
                fn: createSvc,
                args: [_self, username, svc.service]
            });

            return updateVolumesStatus(svc,false);

        }).then(function () {
            return new Promise(function (_resolve, _reject) {
                var cond = {};
                if (svc.composeId !== undefined)
                    cond = {composeId: svc.composeId, app: svc.app};
                else
                    cond = {username: username, app: svc.app};
                svcMetaInfoDB.delWithParam(null, cond, function (err) {
                    if (err) {
                        _reject(myRet(false,err.message));
                    } else {
                        _resolve(myRet(true));
                    }
                });
            });
        }).then(function () {
                //all finish
                //async to   clean volume
                new Promise(function(res,rej){
                    svc.username = username ;
                    clearHomeLessHostPathRecord( svc ) ;

                    //delete volume
                    if(isDelVolume)
                        deleteVolumeBySvc(svc);

                    res(true ) ;
                }).catch( function(e){
                    res(true ) ;
                });

                resolve(true);
            }).catch(function (e) {
                logger.info("deleteStateLessApp need to rollback:"+ e.error||e.message);
                rollback(_self, _rollback_stack).then(function () {
                    reject(JSON.parse(e));
                }).catch(function (err) {
                    //rollback happened error
                    logger.info("deleteStateLessApp rollback err:" + err);
                    var rejectResult = e;
                    if (typeof(e) === "string")
                        rejectResult = JSON.parse(e);
                    reject( TxSupport().rollBackFailure( rejectResult )  );
                });

        });
    });
}

function addInvalidMKeyIntoPetset(svc) {
    var newSvc = deepcopy(svc);
    var i = 11;
    i = i + 1;
    newSvc.petset.spec.template.metadata.annotations["pod.alpha.kubernetes.io/initialized"] = "true";
    return newSvc;
}

function isHostPathSvc(svc){
    if( svc.volumeType && svc.volumeType == "hostPath" ){
       return true ;
    }
    return false ;
}
function mongosvcformat(svc){
    var _Obj = {};
    if(svc.petset){
        _Obj = svc.petset;
    }else
        _Obj = svc.rc;

    if(_Obj.spec.template.metadata.annotations) {
        _Obj.spec.template.metadata.annotations.affinity = '';
        _Obj.spec.template.metadata.annotations.affinity = _Obj.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity'];
        delete _Obj.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity'];
    }
    delete svc.node.configmap;
}
function setHostpath(svc,app,username){
    if(svc.rcnum!==1&&svc.node.volumes){
        var length=svc.node.volumes.length;
        var names=[];
        for(var i=0;i<length;i++) {
            var len=svc.node.volumes.length;
            var name=(svc.node.volumes[i].name.split('-')[0])||(svc.node.volumes[i].name);
            if (!svc.node.volumes[i].isShared&&names.indexOf(name)<0) {
                names.push(name);
                // var id=(svc.node.volumes[i].volumeId.split('-')[2])||(svc.node.volumes[i].volumeId.split('-')[1]);
                for(var j=0;j<svc.rcnum-1;j++) {
                    svc.node.volumes[len] = {};
                    svc.node.volumes[len].type = 'hostpath';
                    svc.node.volumes[len].name = name + '-' + app + '-' + (j + 1);
                    svc.node.volumes[len].mountName = name;
                    svc.node.volumes[len].volumeId = config.highIOdir + '/' + username + '/' + app + '/' + name + '-' + (j + 1)
                }
            }
        }
    }
    var vs=[];
    var ids=[];
    for (var i=0; i< svc.node.volumes.length;i++) {
        var id='';
        if(svc.node.volumes[i].volumeId.split('-')[2]){
            id=svc.node.volumes[i].volumeId.split('-')[0]+'-'+(svc.node.volumes[i].volumeId.split('-')[2]);
        }else{
            id=svc.node.volumes[i].volumeId.split('-')[0]+'-'+(svc.node.volumes[i].volumeId.split('-')[1]);
        }
        if(ids.indexOf(id)<0){
            ids.push(id);
            vs.push(svc.node.volumes[i]) ;
        }
    }
    svc.node.volumes=vs;
}
function updateApp(_self, username, svc, oldsvc) {
    //var _self = this;

    logger.debug("updateApp begin");
    if(  !isHostPathSvc(  svc ) ){
        svc.volumeType = "";
    }else{
        for (var i in svc.node.volumes) {
            if(svc.node.volumes[i].volumeId){

            }else {
                var pathinfo = hostPath(svc.node.volumes[i].mountTargetPath, svc.app, 0);
                var pat = config.highIOdir+'/' + username + '/' + svc.app + '/' + pathinfo;
                svc.node.volumes[i].volumeId = pat;
                var name=pathinfo.split('-')[0];
                svc.node.volumes[i].name=name;
                svc.node.volumes[i].type = "hostpath";
            }
        }
        setHostpath(svc,svc.app,username);
        }
    var isStateful = isStatefulApp(svc) || (oldsvc.petset !== undefined);
    if (isStateful){
        if(oldsvc.petset)
            return updateStatefulApp(_self,username,svc,oldsvc);
        else
            return ConvertRc2PetsetWhenUpt(_self,username,svc,oldsvc);
    }
    //old update rc mode
    if(updateMode === 0){
        return new Promise(function (resolve, reject) {
            var _rc, _svc;
            _svc = obj2Svc(svc);
            var _rollback_stack = [];
            var promise;
            //judge for port and type
            var oldPortsCopy = deepcopy(oldsvc.node.ports);
            oldPortsCopy.forEach(function (portInfo) {
                delete portInfo.address;
            });
            var isServiceUpdate = !isEqual(oldPortsCopy, svc.node.ports);
            if (isServiceUpdate) {
                promise = reSizeReplicas(_self, username, oldsvc.app + '-v' + oldsvc.ver, 0).then(function () {

                    _rollback_stack.push({
                        fn: reSizeReplicas,
                        args: [_self, username, oldsvc.app + '-v' + oldsvc.ver, oldsvc.rcnum]
                    });

                    return deleteRc(_self, username, oldsvc.rc,0);
                }).then(function () {

                    _rollback_stack[0] = {
                        fn: createRc,
                        args: [_self, username, oldsvc.rc]
                    };

                    return deleteSvc(_self, username, oldsvc.app);
                }).then(function () {

                    _rollback_stack.push({
                        fn: createSvc,
                        args: [_self, username, oldsvc.service]
                    });

                    //create svc
                    return createSvc(_self, username, _svc);
                }).then(function (svcResponse) {

                    updatePublicAddress(_self,username,svcResponse, svc);
                    if( isHostPathSvc( svc  )){
                        return formatVolumes(username, svc.app, svc);
                    }else{
                        return _createStorageSvcInOwnNS(_self, username, svc.node.volumes);
                    }
                }).then(function () {
                    if( isHostPathSvc( svc ) ){
                        return insertHostPathInfo( svc ,true)
                    }else if(svc.node.config.name){
                        return _createConfigMapAndVolume(_self,username,svc);
                    } else {
                        return emptyPromise();
                    }
                }).then(function () {
                    return createVolumes(username,_svc.app,svc.node.volumes, svc.rcnum)
                }).then(function () {

                    _rollback_stack.push({
                        fn: deleteSvc,
                        args: [_self, username, _svc]
                    });

                    _rc = obj2Rc(svc,_self);
                    return createRc(_self, username, _rc);
                }).then(function () {
                    return fixRcNumByServiceStatue(_self, username, _rc, oldsvc);
                }).then(function () {

                    _rollback_stack.push({
                        fn: deleteRc,
                        args: [_self, username, _rc,0]
                    });

                    return updateDB(oldsvc);

                }).then(function () {
                    // no rollback svc

                    return updateVolumeStatusExt(svc);

                });
            } else {
                // Keep original port info. 端口信息未变
                svc.node.ports = oldsvc.node.ports;
                promise = reSizeReplicas(_self, username, oldsvc.app + '-v' + oldsvc.ver, 0).then(function () {

                    _rollback_stack.push({
                        fn: reSizeReplicas,
                        args: [_self, username, oldsvc.app + '-v' + oldsvc.ver, oldsvc.rcnum]
                    });
                    return deleteRc(_self, username, oldsvc.rc,0);
                }).then(function () {

                    _rollback_stack[0] = {
                        fn: createRc,
                        args: [_self, username, oldsvc.rc]
                    };
                    try{
                        if( isHostPathSvc( svc )){
                            return formatVolumes(username, svc.app, svc);
                        }else{
                            return _createStorageSvcInOwnNS(_self, username, svc.node.volumes);
                        }
                    }catch (exec){
                        logger.error( "createStorageSvcInOwnNS or not:"+exec) ;
                    }

                }).then(function () {
                    logger.debug("createVolumes passBy") ;
                    if( isHostPathSvc( svc ) ){
                        return insertHostPathInfo( svc ,true)
                    }else if(svc.node.config.name){
                        return _createConfigMapAndVolume(_self,username,svc);
                    } else {
                        return emptyPromise();
                    }
                }).then(function () {
                    return createVolumes(username,_svc.app,svc.node.volumes, svc.rcnum)
                }).then(function () {
                    _rc = obj2Rc(svc,_self);
                    logger.debug("createRc passBy") ;
                    return createRc(_self, username, _rc);
                }).then(function () {
                    logger.debug("fixRcNumByServiceStatue passBy") ;
                    return fixRcNumByServiceStatue(_self, username, _rc, oldsvc);
                }).then(function () {
                    _rollback_stack.push({
                        fn: deleteRc,
                        args: [_self, username, _rc,0]
                    });
                    return updateDB(oldsvc);
                }).then(function () {
                    return updateVolumeStatusExt(svc);
                });
            }
            promise.then(function () {
                logger.debug("创建容器过程结束！") ;
                try{
                    clearHomeLessHostPathRecord( svc );
                }catch(ex){
                    logger.warn("clear action failed."+JSON.stringify( ex ) ) ;
                }
                resolve(true);
            }).catch(function (e) {
                logger.error("容器创建异常:"+ JSON.stringify( e ) ) ;
                rollback(_self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function (err) {
                    //rollback happened error
                    logger.info("updateApp rollback err:" + err);
                    reject(  TxSupport().rollBackFailure( e ) );
                });

            });

            var updateDB = function (oldsvc) {

                var stmt={
                    namespace: oldsvc.username,
                    serviceName:oldsvc.app,
                    version:oldsvc.ver
                };
                var data={
                    namespace: oldsvc.username,
                    serviceName:oldsvc.app,
                    version:oldsvc.ver,
                    confName:oldsvc.node.config.name
                };
                confHistoryDB.updateWithParam(null,stmt, data, function (err, ret) {
                    if (err != null){
                        console.log(err);
                        reject(err);
                    } else {
                        logger.debug("Inserted.");
                        resolve(ret);
                    }
                });

                return new Promise(function (_resolve, _reject) {
                    var cond = {};
                    if (svc.composeId !== undefined)
                        cond = {composeId: svc.composeId, app: svc.app};
                    else
                        cond = {username: username, app: svc.app};
                    //update rc object & svc objcect
                    svc.rc = _rc;
                    svc.service = _svc;

                    //set service starting statue
                    if (oldsvc.totalStatue === 'running' || oldsvc.totalStatue === 'error') {
                        svc.totalStatue = 'starting';
                    }
                    mongosvcformat(svc);
                    svcMetaInfoDB.updateField(null, cond, svc, function (err) {
                        if (err) {
                            logger.error("svcMetaInfoDB.updateField() failed ,err="+err) ;
                            _reject(err);
                        } else {
                            _resolve(true);
                        }
                    });
                });
            }//-


        });
    }else{
        /**
         *  old mode will be deprecated
         */

        /**
         *  check if need to update service in k8s
         */
        return new Promise(function (resolve, reject) {
            var _promise = undefined;

            var oldPortsCopy = deepcopy(oldsvc.node.ports);
            oldPortsCopy.forEach(function (portInfo) {
                delete portInfo.address;
            });
            var isServiceUpdate = !isEqual(oldPortsCopy, svc.node.ports);
            if (isServiceUpdate) {
                //TODO update service
                var _svc = obj2Svc(svc);
                _promise = updateSvc(_self, username, _svc);

            }
            //TODO update rc
        });
    }
}

function updateStatefulApp(_self,username,svc,oldsvc){
    //var _self = this;
    return new Promise(function (resolve, reject) {
        var _petset, _svc;
        _svc = obj2Svc(svc);
        var _rollback_stack = [];
        var promise;
        //judge for port and type
        var oldPortsCopy = deepcopy(oldsvc.node.ports);
        var _id = svc.app;
        oldPortsCopy.forEach(function(portInfo){
            delete portInfo.address;
        });
        var isServiceUpdate = !isEqual(oldPortsCopy, svc.node.ports);
        var _volumes_for_delete = getVolumesDiff(oldsvc.node.volumes,svc.node.volumes);
        var _volumes_for_create = getVolumesDiff(svc.node.volumes,oldsvc.node.volumes);
        promise = deletePetset(_self,username,svc.app).then(function(){

            var newSvc = addInvalidMKeyIntoPetset(oldsvc);
            _rollback_stack.push({
                fn:createPetset,
                args:[_self,username, newSvc.petset]
            });

            return deletePods(_self,username,"app=" + oldsvc.app);
        }).then(function() {
            return deletePVCs(_self,username, _volumes_for_delete);
        }).then(function() {
            _rollback_stack.push({
                fn:createPVCs,
                args:[_self, username, _id, _volumes_for_delete,undefined,oldsvc.petset]
            });
            return deletePVs(_self,username, _volumes_for_delete);
        }).then(function() {
            _rollback_stack.push({
                fn: createPVs,
                args: [_self, username, _id, _volumes_for_delete,undefined,oldsvc.petset]
            });
            if (isServiceUpdate)
                return deleteSvc(_self,username, _id);
        }).then(function () {
            if (isServiceUpdate) {
                _rollback_stack.push({
                    fn: createSvc,
                    args: [_self,username, oldsvc.service]
                });
                //create svc
                return createSvc(_self,username, _svc);
            }
        }).then(function (svcResponse) {
            if (isServiceUpdate) {
                updatePublicAddress(_self, username,svcResponse, svc);
                _rollback_stack.push({
                    fn: deleteSvc,
                    args: [_self, username, _svc]
                });
            }else{
                svc.node.ports = oldsvc.node.ports;
            }
            return _createStorageSvcInOwnNS(_self,username, svc.node.volumes);
        }).then(function(){
            if(svc.node.config.name){
                return _createConfigMapAndVolume(_self,username,svc);
            } else {
                return emptyPromise();
            }
        }).then(function () {
            return createVolumes(username,_id,_volumes_for_create, svc.rcnum)
        }).then(function() {
            _rollback_stack.push({
                fn:deleteVolumes,
                args:[username,svc.app, _volumes_for_create, svc.rcnum]
            });
            return createPVs(_self, username, _id, _volumes_for_create,undefined,oldsvc.petset);
        }).then(function(){
            _rollback_stack.push({
                fn:deletePVs,
                args:[_self, username, _volumes_for_create]
            });
            return createPVCs(_self, username, _id, _volumes_for_create,undefined,oldsvc.petset);
        }).then(function(){
            _rollback_stack.push({
                fn:deletePVCs,
                args:[_self, username, _volumes_for_create]
            });

            joinScaleableVolume(svc.node.volumes,_volumes_for_create);
            _petset = obj2PetSet(svc);
            return createPetset(_self,username, _petset);
        }).then(function(){
            _rollback_stack.push({
                fn: deletePetset,
                args: [_self,username, _petset]
            });
            return fixRcNumByServiceStatue(_self,username, _petset,oldsvc, true);
        }).then(function () {
            return updateDB(oldsvc);
        }).then(function(){
            // no rollback svc
            return updateVolumeStatusExt(svc);
        }).then(function(){
            resolve(true);
        }).catch(function (e) {

            rollback(_self,_rollback_stack).then(function(){
                reject(e);
            }).catch(function(err){
                //rollback happened error
                logger.info("updateApp rollback err:"+err);
                reject(e);
            });

        });

        var updateDB = function(oldsvc) {

            var stmt={
                namespace: oldsvc.username,
                serviceName:oldsvc.app,
                version:oldsvc.ver
            };
            var data={
                namespace: oldsvc.username,
                serviceName:oldsvc.app,
                version:oldsvc.ver,
                confName:oldsvc.node.config.name
            };
            confHistoryDB.updateWithParam(null,stmt, data, function (err, ret) {
                if (err != null){
                    console.log(err);
                    reject(err);
                } else {
                    logger.debug("Inserted.");
                    resolve(ret);
                }
            });

            return new Promise(function (_resolve, _reject){
                var cond = {};
                if(svc.composeId !== undefined)
                    cond = {composeId:svc.composeId,app:svc.app};
                else
                    cond = {username:username,app:svc.app};
                //update rc object & svc objcect
                svc.petset = _petset;
                svc.service = _svc;

                //set service starting statue
                if(oldsvc.totalStatue === 'running' || oldsvc.totalStatue === 'error'){
                    svc.totalStatue = 'starting';
                }
                mongosvcformat(svc);
                var prunedSvc = proInvalidMKeyInPetSet(svc);
                svcMetaInfoDB.updateField(null,cond,prunedSvc,function(err){
                    if(err){
                        _reject(err);
                    }else{
                        _resolve(true);
                    }
                });
            });
        }
    });

}

function joinScaleableVolume(volumes_all,volume_create){
    volume_create.forEach(function(volume){
        if(!volume.IsDefault && volumes_all.indexOf(volume) === -1){
            volumes_all.push(volume);
        }
    });
}

function getVolumesDiff(_volumes_1,_volumes_2){
    var _ret = [];
    var _isContain = false;
    for(var i in _volumes_1){
        _isContain = false;
        for(var j in _volumes_2){
            /**
             *  通过对比新旧volume的mountName、isShared，判断是否相同（解决从不共享改成共享后pvc和pv无法删除的bug)
             */
            if(_volumes_2[j].mountName === _volumes_1[i].mountName && _volumes_2[j].isShared === _volumes_1[i].isShared){
                _isContain = true;
                break;
            }
        }
        if(!_isContain)
            _ret.push(_volumes_1[i]);
    }
    return _ret
}

/**
 *   when stateful volume in volume list , change rc to petset,
 *   and delete rc and create petset. Maybe it's not best way  = =!
 *
 * @param _self
 * @param username
 * @param svc
 * @param oldsvc
 * @returns {*}
 * @constructor
 */
function ConvertRc2PetsetWhenUpt(_self,username,svc,oldsvc){
    return _self.deleteAppService(username,oldsvc).then(function(){

        setSvcFieldIntoNode(svc);
        var isStateful = isStatefulApp(svc);
        //debug  set stateful true
        if (isStateful){
            return createStatefulApp(_self,username,svc);
        }else{
            return createStatelessApp( _self, username, svc ) ;
        }

        //return _self.createAppService(username,svc);
    }).then(function(){
        var stmt={
            namespace: oldsvc.username,
            serviceName:oldsvc.app,
            version:oldsvc.ver
        };
        var data={
            namespace: oldsvc.username,
            serviceName:oldsvc.app,
            version:oldsvc.ver,
            confName:oldsvc.node.config.name
        };
        confHistoryDB.updateWithParam(null,stmt, data, function (err, ret) {
            if (err != null){
                console.log(err);
                reject(err);
            } else {
                logger.debug("Inserted.");
                resolve(ret);
            }
        });
    });
}

function createRc(self, ns, rc,force) {
    return new Promise(function (resolve, reject) {
        //logger.debug(ns+":"+rc.metadata.name+" start to create rc") ;
        self.kubeapi.post('namespaces/' + ns + '/replicationcontrollers', rc, function (err, data) {
            if (err) {
                logger.error("[createRc]error happened "+JSON.stringify(data )) ;
                reject(err);
            }
            else if (data.code != undefined && data.kind == 'Status') {
                //ft for create rc
                if (data.reason === 'AlreadyExists'){
                    if(data.code === 409){
                        //means confict when create rc ,set log
                        logger.warn("[createRc]error happened :"+JSON.stringify(data )) ;
                        if( typeof data =="string"){
                            data = JSON.parse( data );
                        }
                        if(data.message.indexOf("object is being deleted") != -1){
                            createRc(self, ns, rc,true).then(function(){
                                resolve();
                            });

                        }else if( force && data.message.indexOf("already exists") != -1){
                            resolve();
                        }else{
                            reject(data.message);
                        }
                    }else
                        resolve();
                }
                else{
                    logger.warn("[createRc]exception happened "+JSON.stringify(data )) ;
                    reject(data.message);
                }
            } else{
                //logger.debug(ns+":"+rc.metadata.name+" start to create rc") ;
                console.log(data);

                //set  uid if success, delete rc depend on it
                rc.metadata.uid = data.metadata.uid;

                resolve(data);
            }

        });
    });
}

//function deleteRc(self, ns, id) {
/**
 *  delete rc in k8s
 * @param self     k8s handler
 * @param ns       request namespaces
 * @param rc       replicationController body (svc.rc)
 * @param gracePeriodSeconds    default undefined
 * @returns {Promise}
 */
function deleteRc(self, ns, rc,gracePeriodSeconds) {
    return new Promise(function (resolve, reject) {
        var id = rc.metadata.name;
        logger.debug(ns+":"+id+" start to delete rc") ;

        /**
         * Must be fulfilled before a deletion is carried out.
         * If not possible, a 409 Conflict status will be returned.
         *
         *  try many times and still return 409,so add fault tolerance in create rc after return 409
         *
         */
        var _delOpt = {
            "preconditions":{
                "uid":rc.metadata.uid
            }
        };

        //seems useless ,so will check it later
        //and comment it first
        /*if(gracePeriodSeconds)
            _delOpt.gracePeriodSeconds = 0;*/

        self.kubeapi.delete('namespaces/' + ns + '/replicationcontrollers/' + id, function (err, data) {
            if (err) {
                logger.error("Can not delete rc"+ns+":"+id+","+ err ) ;
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}

function createSvc(self, ns, svc) {
    return new Promise(function (resolve, reject) {
        logger.debug(ns+":"+svc.metadata.name+" start to create svc");
        self.kubeapi.post('namespaces/' + ns + '/services', svc, function (err, data) {
            if (err) {
                logger.error("can note crate service "+JSON.stringify(svc ) +"\n"+ JSON.stringify(err )) ;
                if(svc.kind === 'Endpoints' && JSON.stringify(err).indexOf('AlreadyExists') !== -1){
                    resolve(myRet(true));
                }else
                    reject(myRet(false,err.message));
            }
            else {
                //已经存在了，说明很有可能是上次创建未完成的，可以继续下去。
                if (data.code != undefined && data.code != 200 && JSON.stringify(data).indexOf('AlreadyExists') === -1) {
                    logger.warn("Can't create service for "+JSON.stringify(data ) ) ;
                    reject(myRet(false,JSON.stringify(data)));
                } else
                    resolve(myRet(true,undefined,data));
            }
        });
    });
}

function deleteSvc(self, ns, id) {
    return _deleteConfigMap(self, ns,id).then(function () {
        return new Promise(function (resolve, reject) {
            logger.debug(ns + ":" + id + " start to delete svc");
            self.kubeapi.delete('namespaces/' + ns + '/services/' + id, function (err, data) {
                if (err) {
                    var _retmsg = "Can't delete service " + ns + ":" + id + "," + err.message
                    logger.error(_retmsg);
                    reject(myRet(false, _retmsg));
                }
                else {
                    resolve(myRet(true, undefined, data));
                }
            });
        });
    });
}

function updateSvc(self,ns,svc){
    return new Promise(function (resolve, reject) {
        self.kubeapi.put('namespaces/' + ns + '/services/'+svc.app,svc, function (err, data) {
            if (err) {
                logger.error("Can't delete service "+ns+":"+svc.app+","+err  ) ;
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}

function createEndpoint(self, ns, ed) {
    return new Promise(function (resolve, reject) {
        self.kubeapi.post('namespaces/' + ns + '/endpoints', ed, function (err, data) {
            if (err) {
                logger.debug("create endpoint failed:" + err);
                reject(myRet(false,err.message));
            }
            else {
                if (data.code != undefined && data.code != 200) {
                    var _retmsg = "create endpoint failed ,because of \n" + JSON.stringify(data);
                    logger.debug(_retmsg);
                    reject(myRet(false,_retmsg));
                } else
                    resolve(myRet(true,null,data));
            }
        });
    });
}

function deleteEndpoint(self, ns, ed) {
    return new Promise(function (resolve, reject) {
        self.kubeapi.delete('namespaces/' + ns + '/endpoints', ed, function (err, data) {
            if (err) {
                reject(myRet(false,err.message));
            }
            else {
                if (data.code != undefined && data.code != 200) {
                    reject(myRet(false,JSON.stringify(data)));
                } else
                    resolve(myRet(true,undefined,data));
            }
        });
    });
}
//check if there is a endpoint under the namespace(default is username)
function chkIsEndPointExist(self, ns, svcId) {
    return new Promise(function (resolve, reject) {
        self.kubeapi.get('namespaces/' + ns + '/endpoints/' + svcId, function (err, data) {
            if (err) {
                logger.info(err);
                reject(myRet(false,err.message));
            }
            else {
                if (data.code != undefined && data.code != 200) {
                    var _retmsg = ns + ",check endpoint ret.data:" + JSON.stringify(data)
                    logger.debug(_retmsg);
                    resolve(myRet(false,_retmsg));
                } else
                    resolve(myRet(true));
            }
        });
    });
}

function _createJob(self, svcId, job) {
    //TODO
    //http://115.28.57.195:8082/apis/batch/v1/namespaces/default/jobs
    return new Promise(function (resolve, reject) {

        chkJobExist(self, svcId).then(function (ret) {

            if (ret.ret === false) {
                return _createJobReq(self, job);
            } else {
                return new Promise(function (_resolve, _reject) {
                    _deleteJobReq(self, svcId).then(function (ret) {
                        if (ret.ret === true)
                            return _createJobReq(self, job);
                        else
                            reject('delete job error happened');
                    }).then(function (ret) {
                        _resolve(ret);
                    }).catch(function (err) {
                        reject(err);
                    });
                });
            }
        }).then(function (ret) {
            resolve(ret);
        }).catch(function (err) {
            reject(err);
        });
    });
}

function _createJobReq(self, job) {

    return new Promise(function (resolve, reject) {

        //var _url = 'http://' + config.kubernetes.host + ':' + config.kubernetes.port + '/apis/batch/v1/namespaces/default/jobs';
        var _url = 'http://' + self.host + ':' + self.port + '/apis/batch/v1/namespaces/default/jobs';

        _req._request(_url, 'POST', {"Content-Type": "application/json"}, JSON.stringify(job), function (data, result, response) {
            if (data !== null) {
                resolve(data.data);
            }
            else {
                var _err = JSON.parse(result);
                if (_err.reason === 'AlreadyExists')
                    resolve(true);
                else
                    reject(_err);
            }
        });
    });

}

function _deleteJob(self, svcId) {
    //TODO
    return _deleteJobReq(self, svcId);
}

function _deleteJobReq(self, job_id) {
    return new Promise(function (resolve, reject) {

        //var _url = 'http://' + config.kubernetes.host + ':' + config.kubernetes.port + '/apis/batch/v1/namespaces/default/jobs/'+job_id;
        var _url = 'http://' + self.host + ':' + self.port + '/apis/batch/v1/namespaces/default/jobs/' + job_id;

        _req._request(_url, 'DELETE', {"Content-Type": "application/json"}, '', function (data, result, response) {
            if (data !== null) {
                var _ret = JSON.parse(data.data);
                if (_ret.status === 'Success' && _ret.code === 200) {
                    resolve(myRet(true));
                }
            }
            else if (result !== null) {
                var _ret = JSON.parse(result);

                // delete not exist
                if (_ret.status === 'Failure' && _ret.code === 404 && _ret.message.indexOf('not found') !== -1) {
                    resolve(myRet(true));
                }
                else {
                    resolve(myRet(false));
                }
            }
            else {
                reject(myRet(false,response));
            }
        });
    });

}

function chkJobExist(self, job_id) {
    //TODO
    return new Promise(function (resolve, reject) {

        //var _url = 'http://'+config.kubernetes.host+':'+config.kubernetes.port+'/apis/batch/v1/namespaces/default/jobs/'+job_id;
        var _url = 'http://' + self.host + ':' + self.port + '/apis/batch/v1/namespaces/default/jobs/' + job_id;

        _req._request(_url, 'GET', {"Content-Type": "application/json"}, '', function (data, result, response) {
            if (data !== null) {
                var _ret = JSON.parse(data.data);
                if (_ret.status === 'Failure') {
                    resolve(myRet(false));
                } else {
                    resolve(myRet(true));
                }
            }
            else {
                var _ret = JSON.parse(result);
                if (_ret.status === 'Failure')
                    resolve(myRet(false));
                else
                    reject(myRet(false,result));
            }
        });

    });

}

function emptyPromise(data) {
    if(data === undefined ){
        var data = {};
    }
    return new Promise(function (resolve,reject) {
        resolve(data) ;
    }) ;
}

/**
 *  change the  --replicas in the rc ,so to stop /start/add pod in k8s
 * @returns {Promise}
 */
function reSizeReplicas(self,ns,id,num, orgNum, svc,op){

    var isPetset = (svc && svc.petset) ? true:　false;
    //petset replicas
    logger.debug("[%s] app [%s] set replicas to %d",isPetset?"petset":"default",id,num);
    var newSvc = deepcopy(svc);
    if (isPetset) {
        var _rollback_stack = [];
        return new Promise(function (resolve, reject) {
            var app = svc.app;
            var promise = emptyPromise();
            /**
             *  scale
             *  1.modify num
             *  2.check scale number
             *    if scale up, create pvc and pv , otherwise delete them
             */
            if (op === "modifyReplicas") {
                newSvc.rcnum = num;
                newSvc.petset.spec.replicas = num;
                if (num > orgNum) {
                    //增加pv和pvc
                    var promises = [];
                    var volumes = newSvc.node.volumes;
                    var _createlist = [];
                    var _createVolMap = {};
                    var encodeMountPath = undefined;

                    /**
                     *  scale up steps:
                     *  1.create stateful volume
                     *  2.update volume array
                     *  3.create pv that scale up part  4.create pvc that scale up part
                     */
                    volumes.forEach(function (one,index) {

                        if (one.isShared === false) {

                            /**
                             *  key is volume name
                             */
                            if(_createVolMap[one.name] === undefined){
                                _createVolMap[one.name] = one;
                                promises.push(createStatefulVolume(ns, app, one, num, orgNum));
                                _createlist.push(one);
                            }
                        }
                    });
                    promise = Promise.all(promises).then(function (retlst) {

                        _rollback_stack.push({
                            fn: deleteStatefulVolumeByScale,
                            args: [ns, app, encodeMountPath, num, orgNum]
                        });

                        var _ret = true;
                        retlst.forEach(function (one,index) {
                            var vol = _createlist[index];
                            //var mountArray = vol.mountArray||[];
                            one.forEach(function (volData) {
                                if (!volData.ret)
                                    _ret = false;
                                else
                                    //mountArray.push(volData.data);
                                    volumes.push(volData.data);
                            });
                        });
                        if (_ret) {
                            return updateVolumeStatusExt(newSvc);
                            /*return new Promise(function (resolve, reject) {
                                resolve(true);
                            });*/
                        }
                        else {
                            return new Promise(function (resolve, reject) {
                                reject(myRet(false, 'create vol error'));
                            });
                        }
                    }).then(function () {

                        //need to put rollback before do after add fault tolerance
                        _rollback_stack.push({
                            fn: deletePVsScaleDown,
                            args: [self, ns, newSvc.node, orgNum]
                        });

                        return createPVs(self, ns, svc.app, newSvc.node.volumes, orgNum);
                    }).then(function () {

                        _rollback_stack.push({
                            fn: deletePVCsScaleDown,
                            args: [self, ns, newSvc.node, orgNum]
                        });

                        return createPVCs(self, ns, svc.app, newSvc.node.volumes, orgNum);
                        //return true;
                    });
                } else {

                    /**
                     * scale down steps:
                     * 1.scale down pvcs
                     * 2.scale down pv
                     * 3.remove  mount array that scale down part in volumes
                     *
                     */

                    var volumes = newSvc.node.volumes;
                    promise = deletePVCsScaleDown(self, ns, newSvc.node, num).then(function () {
                        _rollback_stack.push({
                            fn: createPVCs,
                            args: [self, ns, svc.app, newSvc.node.volumes, num]
                        });
                        return deletePVsScaleDown(self, ns, newSvc.node, num);
                    }).then(function () {
                        _rollback_stack.push({
                            fn: createPVs,
                            args: [self, ns, svc.app, newSvc.node.volumes, num]
                        });

                        var volumes = newSvc.node.volumes;
                        /*var _poptimes = orgNum;
                        volumes.forEach(function (one) {
                            if (one.isStateful) {
                                while (_poptimes-- > num) {
                                    one.mountArray.pop();
                                }
                            }
                        });*/
                        for(var _i=0;_i<volumes.length;_i++){
                            if (!volumes[_i].isShared) {
                                var _idx = getVolumeIndex(volumes[_i]);
                                if(_idx >= num){
                                    volumes.splice(_i,1);
                                    _i--;
                                }
                            }
                        }
                        return updateVolumeStatusExt(newSvc);

                    });

                }
            }
            /**
             *  start ,stop
             *  1.only set petset replicas number
             */
            else if (op === "start" || op === "stop") {
                newSvc.petset.spec.replicas = num;
                promise = new Promise(function (resolve, reject) {
                    resolve(true);
                });
            }
            else {
                console.log("catch other op[" + op + "]");
            }

            /**
             *  request k8s petset api
             */
            promise.then(function () {
                    return _replicasPetSet(self, ns, svc, num, op);
                })
                /**
                 *  update the db
                 *  1.all previous steps done, and update db info
                 */
                .then(function () {

                    return new Promise(function (resolve, reject) {
                        var cond = {username: ns, app: svc.app};
                        //force delete should not update in db;
                        if (newSvc.forceDelete)
                            delete newSvc["forceDelete"];
                        svcMetaInfoDB.updateWithParam(null, cond, newSvc, function (err) {
                            if (err) {
                                reject(myRet(false, err.message));
                            } else {
                                resolve(myRet(true));
                            }
                        });
                    });
            })
            .then(function(){
                if( isHostPathSvc( svc )){
                    //需要格式化成能够让下面代码支持的格式
                    return formatVolumes(ns, svc.app, newSvc) ;
                }else{
                    return emptyPromise() ;
                }
            }).then(function(){
                /**
                 *   update volume state in storage info table
                 */
                return updateVolumesStatus(newSvc,true);
            }).then(function(ret){
                resolve(myRet(true,"",newSvc));
            }).catch(function(e) {
                rollback(self, _rollback_stack).then(function () {
                    reject(e);
                }).catch(function(err){
                    //rollback happened error,log it and throw root error
                    logger.error("reSizeReplicas rollback err:"+err.error);
                    reject(e);
                });
            })
        });
    }
    /**
     *  normal replicationControl replicas
     *  1.set replicationSchema to k8s api
     *  2.check force delete flag, and delete pods after api callback
     *
     *  attention:
     *  1.if newSvc is undefined means just set replicas number
     */
    else {
        return new Promise(function(resolve, reject){

            _replicasRc(self,ns,id,newSvc,num,op).then(function(ret){

                if(newSvc){
                    //start or stop not to set rcnum
                    if(op !== 'start' && op !== 'stop')
                        newSvc.rcnum = num;
                    newSvc.rc.spec.replicas = num;
                    var cond = {username: ns, app: svc.app};
                    //force delete should not update in db;
                    if ( newSvc.forceDelete)
                        delete newSvc["forceDelete"];
                    svcMetaInfoDB.updateWithParam(null, cond, newSvc, function (err) {
                        if (err) {
                            reject(myRet(false, err.message));
                        } else {
                            resolve(myRet(true,"",newSvc));
                        }
                    });
                }else
                    resolve(ret);
            }).catch(function(e){
                reject(e);
            });
        });
    }
}

function _replicasPetSet(self,ns,svc,num){
    return new Promise(function (resolve, reject) {
        var replicationSchema = JSON.stringify({spec: {replicas: parseInt(num)}});
        var _url = 'http://' + self.host + ':' + self.port + '/apis/apps/v1alpha1/namespaces/' + ns + '/petsets/' + svc.app;
        _req._request(_url, 'PATCH', {"Content-Type": "application/merge-patch+json"}, replicationSchema, function (data, result, response) {
            if (data !== null) {
                //async to deal with delete pod
                if (svc && svc.forceDelete) {
                    libK8s.prototype.deletePod(ns, svc, self,true).then(function (ret) {
                        logger.debug("[Success]forceDelete pod");
                    }).catch(function (err) {
                        logger.debug("[Exception]forceDelete pod " + err.error);
                    });
                }
                resolve(data.data);
            }
            else {
                var jsonObjectResult = JSON.parse(result);
                reject(myRet(false,"",jsonObjectResult));
            }
        });
    });
}

/**
 *  request k8s api for replicationcontrol
 *  1.send replicationSchema to modify replicas
 *  2.check force delete flag, and delete pods after api callback
 */
function _replicasRc(self,ns,id,svc,num,op){
    return new Promise(function (resolve, reject) {
        var replicationSchema = JSON.stringify({spec: {replicas: parseInt(num)}}) ;
        var _url = 'http://' + self.host + ':' + self.port + '/api/v1/namespaces/' + ns + '/replicationcontrollers/' + id;
        _req._request(_url, 'PATCH', {"Content-Type": "application/merge-patch+json"}, replicationSchema, function (data, result, response) {
            if (data !== null) {
                logger.debug( "reSizeReplicas:"+JSON.stringify( data )  ) ;
                if(svc && svc.forceDelete){
                    libK8s.prototype.deletePod(ns,svc ,self,true).then(function (ret) {
                        logger.debug( "[Success]forceDelete pod" ) ;
                    }).catch(function (err) {
                        logger.debug( "[Exception]forceDelete pod "+err.error ) ;
                    }) ;
                }
                resolve(myRet(true,"",svc));
            }
            else {
                //return true when service delete

                /**
                 *  if operator rc return  not found when delete or stop service,
                 *  only delete orphan pod and return success
                 *
                 *  otherwise,throw error to upside
                 */
                var jsonObjectResult = JSON.parse(result);
                if(op === 'delete' || op === 'stop'){
                    logger.info("[reSizeReplicas]"+op+' rc:'+jsonObjectResult.message) ;
                    if(jsonObjectResult.message.indexOf("not found") !== -1 ){
                        //delete orphan pods
                        var arr = id.split("-v");
                        deletePods(self,ns,"app=" + arr[0]+",version="+arr[1]) ;
                    }
                    resolve(myRet(true));
                }else{
                    if( !op ){
                        op="start" ;
                    }
                    logger.error("[reSizeReplicas][#%s]Resize replicas failed,exception: ",op,result ) ;
                    reject(myRet(false,jsonObjectResult.message,jsonObjectResult.message));
                }
            }
        });
    });
}

function reSizeRcForGrayUpt(self, ns, oldId, newId, num) {
    return new Promise(function (resolve, reject) {
        try {
            var _i = 0;
            promiseWhile(
                function () {
                    return num > 0;
                },
                function () {
                    return new Promise(function (_resolve, _reject) {
                        reSizeReplicas(self, ns, oldId, --num).then(function () {
                            return reSizeReplicas(self, ns, newId, ++_i);
                        }).then(function () {
                            setTimeout(function () {
                                console.log('old ver:' + oldId + ' , num:' + num + ', new ver:' + newId + ' , num:' + _i);
                                _resolve();
                            }, grayUdpIntervalSec * 1000);
                        }).catch(function (e) {
                            _reject(e);
                        });
                    });
                }
            ).then(function () {
                console.log("done");
                resolve();
            }).catch(function (e) {
                reject(e);
            });
        } catch (e) {
            reject(e);
        }

    });
}

function _createPullImageSecret(self,ns,secret){
    return _createSecret(self,ns,_getPullImgSecretJson(ns,secret));
}

function _createCephSecret(self,ns){
    return _createSecret(self,ns,_getCephSecretJson(ns));
}
/**
 * 更新secret用于用户pull镜像的认证
 * ns :namespace
 * secret :secret of user
 */
function patchPullImageSecret(self,ns,secret){

    return new Promise(function(resolve, reject){
        var _url = 'http://' + self.host + ':' + self.port + '/api/v1/namespaces/' + ns + '/secrets/pullsecret';
        try{
            _req._request(_url, 'PATCH', {"Content-Type": "application/merge-patch+json"}, JSON.stringify(_getPullImgSecretJson(ns,secret) ), function (data, result, response) {
                if (data !== null) {
                    logger.debug( "_patchPullImageSecret-success:"+JSON.stringify( data )  ) ;
                    if( data.statusCode ===200 ){
                        resolve(data.data);
                    }else{
                        reject( data ) ;
                    }
                }
                else {
                    reject({ "message":"failed" } ) ;
                    logger.info("_patchPullImageSecret-fail--"+JSON.stringify( result ));
                    //return true when service delete
                }
            });//-
        }catch(err){
            logger.error("#patchPullImageSecret:"+ JSON.stringify( err) ) ;
            reject(err) ;
        }
    });
}

function _createSecret(self,ns,json){

    return new Promise(function(resolve, reject){
        self.kubeapi.post('namespaces/'+ns+'/secrets',json,function(err, data){
            if(err){
                reject(err);
            }
            else{
                if(data.code != undefined && data.code != 200){
                    //if exist return true
                    if(data.reason === 'AlreadyExists'){
                        resolve(data);
                    }else{
                        reject(data);
                    }
                }else
                    resolve(data);
            }
        });
    });
}

function _getPullImgSecretJson(ns,secret){

    var _json = {
        "auths":{
            /*"naturecloud.io":{
                "auth":secret
            }*/
        }
    };
    _json.auths[config.register.outaddr] = {
        "auth":secret
    };

    var _secret_json_base64 = new Buffer(JSON.stringify(_json)).toString('base64');

    var _ret = {
        "apiVersion": "v1",
        "kind": "Secret",
        "metadata": {
            "name": "pullsecret",
            "namespace":ns
        },
        "data": {
            ".dockerconfigjson": _secret_json_base64
        },
        "type": "kubernetes.io/dockerconfigjson"
    }
    return _ret;
}

function _getCephSecretJson(ns){

    var _ret = {
        "apiVersion": "v1",
        "kind": "Secret",
        "metadata": {
            "name": "ceph-secret",
            "namespace":ns
        },
        "data": {
            "key": "QVFCMTZWMVZvRjVtRXhBQTVrQ1FzN2JCajhWVUxSdzI2Qzg0SEE9PQ=="
        },
        "type": "kubernetes.io/rbd"
    }
    return _ret;
}

/**
 * 停止Pod（优雅的停止，会有一段时间预留来clear up）
 *   Because pods represent running processes on nodes in the cluster,
     it is important to allow those processes to gracefully terminate
     when they are no longer needed (vs being violently killed with a
     KILL signal and having no chance to clean up). Users should be
     able to request deletion and know when processes terminate,
     but also be able to ensure that deletes eventually complete.
     When a user requests deletion of a pod the system records the intended grace period
     before the pod is allowed to be forcefully killed,
     and a TERM signal is sent to the main process in each container.
     Once the grace period has expired the KILL signal is sent to those processes
     and the pod is then deleted from the API server. If the Kubelet or the container manager
     is restarted while waiting for processes to terminate, the termination will be retried
     with the full grace period.
 */
function getPods(self, ns, cond) {
    return new Promise(function (resolve, reject) {
        self.kubeapi.get('namespaces/' + ns + '/pods?labelSelector=' + cond, function (err, data) {
            /*if( data == undefined ){
                var data = {"message":"no data returned !"} ;
            }*/
            if (err) {
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}
/**
 * get the pod by podName
 * @returns {Promise}
 */
function getPodsByName(self, ns, name) {
    return new Promise(function (resolve, reject) {
        self.kubeapi.get('namespaces/' + ns + '/pods/'+name, function (err, data) {
            if (err) {
                if( err ){
                    logger.error("Can't get pod info for :"+JSON.stringify( err )) ;
                }else{
                    logger.error("Can't get pod info for unknow reason") ;
                }
                reject(err);
            }
            else {
                resolve(data);
            }
        });
    });
}

function deletePods(self,ns,cond,force){
    return new Promise(function(resolve, reject){
        var _delOpt = {};
        if(force){
            _delOpt.gracePeriodSeconds=0;
        }
        getPods(self,ns,cond).then(function (data){
            for(var index in data.items) {
                var name = data.items[index].metadata.name;
                self.kubeapi.deleteWithBody('namespaces/' + ns + '/pods/' + name, _delOpt, function (err, data) {
                    if (err) {
                        logger.error("[deletePods]Can not delete pod:" + cond);
                        reject(myRet(false, err.message));
                    }
                    else {
                        logger.debug("[deletePods]Delete pod:" + cond);
                        //
                    }
                });
            }
            resolve(myRet(true, "", data));
        }).catch(function (err) {
            reject(err);
        });
    });
}

function fixRcNumByServiceStatue(self, ns, rc, svc) {
    return new Promise(function (resolve, reject) {
        if (svc.totalStatue === 'stop') {
            reSizeReplicas(self, ns, rc.metadata.name, 0,0,svc).then(function () {
                resolve(true);
            }).catch(function (err) {
                logger.error("fixRcNumByServiceStatue.err"+err) ;
                reject(err);
            });
        } else {
            resolve(true);
        }

    });
}
//it will parser more in future
function obj2GlusterfsPv(obj){
    return {
        "kind": "PersistentVolume",
        "apiVersion": "v1",
        "metadata": {
            "name": obj.name,
            "type":"glusterfs"
        },
        "spec": {
            "accessModes": [
                "ReadWriteMany"
            ],
            "capacity": {
                "storage": obj.size + "Mi"
            },
            glusterfs: {
                endpoints: "glusterfs-cluster",
                path: obj.path
            }
        }
    }
}

function obj2CephPv(ns,obj){

    var _access_mode = obj.isShare?"ReadOnlyMany":"ReadWriteMany";
    return {
        "kind": "PersistentVolume",
        "apiVersion": "v1",
        "metadata": {
            "name": obj.name,
            "type":"ceph"
        },
        "spec": {
            "accessModes": [
                _access_mode
            ],
            "capacity": {
                "storage": obj.size + "Mi"
            },
            rbd: {
                "monitors": config.cephMeta.monitors,
                "pool": ns,
                "image": obj.path,
                "fsType": "ext4",
                "readOnly":obj.isShare
            }
        }
    }
}

function obj2LocalPathPv(obj){
    return {
        "kind": "PersistentVolume",
        "apiVersion": "v1",
        "metadata": {
            "name": obj.name,
            "type":"local"
        },
        "spec": {
            "accessModes": [
                "ReadWriteOnce"
            ],
            "capacity": {
                "storage": obj.size + "Mi"
            },
            hostPath: {
                path: obj.path
            }
        }
    }
}

function obj2pvc(obj){
    var _access_mode = obj.isShare?"ReadOnlyMany":"ReadWriteMany";
    return {
        "kind": "PersistentVolumeClaim",
        "apiVersion": "v1",
        "metadata": {
            "name": obj.name
        },
        "spec": {
            "accessModes": [
                _access_mode
            ],
            "resources": {
                "requests": {
                    "storage": obj.size+"Mi"
                }
            },
            "volumeName": obj.name
        }
    }

}

function setHostPathRc(obj,rc){
    if (obj.volumes !== undefined) {
        rc.spec.template.spec.containers[0].volumeMounts = [];
        //record volume
        var _volumeMap = {};

        //set container part
        for (var _i = 0; _i < obj.volumes.length; _i++) {
            rc.spec.template.spec.containers[0].volumeMounts.push({
                "mountPath": obj.volumes[_i].mountTargetPath,
                "name": obj.volumes[_i].name
            });

            //put volume in list
            if (!_volumeMap.hasOwnProperty(obj.volumes[_i].name))
                _volumeMap[obj.volumes[_i].name] = obj.volumes[_i];
        }

        //set volumes part
        rc.spec.template.spec.volumes = [];
        for (var name in _volumeMap) {
            //_volumeMap[name].volumeId format like a-b-c
            // so need transform to a/b/c
            var hostPath = _volumeMap[name].volumeId;
            rc.spec.template.spec.volumes.push({
                "name": name,
                "hostPath": {
                    "path": hostPath
                }
            });
        }
        rc.spec.template.spec.nodeSelector={};
        rc.spec.template.spec.nodeSelector.highIO=config.highIO;
    }
}

function setGlusterfsRc(obj,rc){
    logger.debug("create a rc with volume type of glusterfs");
    if (obj.volumes !== undefined) {
        rc.spec.template.spec.containers[0].volumeMounts = [];

        //record volume
        var _volumeMap = {};

        //set container part
        for (var _i = 0; _i < obj.volumes.length; _i++) {
            rc.spec.template.spec.containers[0].volumeMounts.push({
                "mountPath": obj.volumes[_i].mountTargetPath,
                "name": obj.volumes[_i].mountObj.name
            });

            //put volume in list
            if (!_volumeMap.hasOwnProperty(obj.volumes[_i].mountObj.name))
                _volumeMap[obj.volumes[_i].mountObj.name] = obj.volumes[_i].mountObj;
        }

        //set volumes part
        rc.spec.template.spec.volumes = [];
        for (var name in _volumeMap) {
            rc.spec.template.spec.volumes.push({
                "name": name,
                "glusterfs": {
                    "endpoints": "glusterfs-cluster",
                    "path": _volumeMap[name].volumeId
                }
            });
        }
    }
}

function setCephRc(obj,rc,ns){
    logger.debug("create a rc with volume type of ceph");
    if (obj.volumes !== undefined) {
        rc.spec.template.spec.containers[0].volumeMounts = [];

        //record volume
        var _volumeMap = {};

        //set container part
        for (var _i = 0; _i < obj.volumes.length; _i++) {
            rc.spec.template.spec.containers[0].volumeMounts.push({
                "mountPath": obj.volumes[_i].mountTargetPath,
                "name": obj.volumes[_i].name
            });

            //put volume in list
            if (!_volumeMap.hasOwnProperty(obj.volumes[_i].name))
                _volumeMap[obj.volumes[_i].name] = obj.volumes[_i];
                /*//isShared is for ceph set ReadOnly
                _volumeMap[obj.volumes[_i].mountObj.name].isShared = obj.volumes[_i].isShared;*/
        }

        //set volumes part
        rc.spec.template.spec.volumes = [];
        for (var name in _volumeMap) {
            var readonly = _volumeMap[name].isShared || false;
            rc.spec.template.spec.volumes.push({
                "name": name,
                "rbd": {
                    "monitors": config.cephMeta.monitors,
                    "pool": ns,
                    "image": _volumeMap[name].volumeId,
                    "fsType": "ext4",
                    "readOnly": readonly
                }
            });
        }
    }
}
/**
 * a service json object which can run under k8s by 'kubectl create -f xx.json'
 */
function obj2Svc(svc, headless) {
    var composeId = svc.composeId;
    var itemId = svc.app;
    var obj = svc.node;

    var portInfos = obj.ports;
    var ports = [];
    var isTCPPublic = false;
    portInfos.forEach(function (portInfo) {
        var port = {
            name: portInfos.indexOf(portInfo).toString(),
            protocol: portInfo.protocol,
            port: 80,
            targetPort: portInfo.targetPort
        }
        if (portInfo.accessProtocol === "HTTP")
           // port.port = portInfo.port;//80;
           port.port =80;

        if (portInfo.accessProtocol === "TCP" && portInfo.isPublic) {
            isTCPPublic = true;
            if(portInfo.targetPort===80){
                port.port = 1;
            }else {
                port.port = portInfo.targetPort;
            }
            if (portInfo.nodePort)
                port.nodePort = portInfo.nodePort;
        }
        ports.push(port);
    });
    if (ports.length == 0) {//添加一个伪端口，用于centos/ubuntu等没有端口的情形。此处理方法是临时性的，当最终确认登录控制台是采用sshd还是其他形式后，再最终确定没有端口时的处理方法
        var port = {
            name: "0",
            protocol: "TCP",
            port: 22,
            targetPort: 22
        }
        ports.push(port);
    }

    var svcType = "ClusterIP";
    if (isTCPPublic)
        svcType = "NodePort";

    var _svc = {
        "kind": "Service",
        "apiVersion": "v1",
        "metadata": {
            "name": "testlog"
        },
        "spec": {
            "selector": {
                "app": "testlog"
            },
            "type": svcType,
            "ports": ports
        }
    };
    if (headless)
        _svc.spec.clusterIP = "None";

    _svc.metadata.name = itemId;
    _svc.metadata.labels = {
        app: itemId
        /*composeName:composeId*/
    };
    if (composeId !== undefined)
        _svc.metadata.labels.composeName = composeId;

    _svc.spec.selector = {
        app: itemId
        /*composeName:composeId*/
    };
    if (composeId !== undefined)
        _svc.spec.selector.composeName = composeId;


    return _svc;
}

function obj2Rc(svc,self) {

    var composeId = svc.composeId;
    var itemId = svc.app;
    var obj = svc.node;
    var _version = svc.ver;
    var rc = {};
    //TypeMeta
    rc.kind = "ReplicationController";
    rc.apiVersion = "v1";

    //ObjectMeta
    rc.metadata = {};
    //name
    rc.metadata.name = itemId + (_version == '' ? '' : '-v' + _version);
    //label
    rc.metadata.labels = {
        app: itemId,
        version: _version
    };
    if (composeId !== undefined)
        rc.metadata.labels.composeName = composeId;
    //Spec
    rc.spec = {};
    //replicas
    rc.spec.replicas = parseInt(svc.rcnum);
    //Selector

    //Template
    //Template metadata
    rc.spec.template = {};
    rc.spec.template.metadata = {
        labels: {
            app: itemId,
            version: _version
        }
    };
    if (composeId !== undefined)
        rc.spec.template.metadata.labels.composeName = composeId;
    //Template spec

    if(self.id !== 'default'){
        var _idx  = obj.image.indexOf('/');
        obj.image = config.register.outaddr + obj.image.substr(_idx);
    }

    rc.spec.template.spec = {};
    // var env={};
    // env.value='';
    // env.value=JSON.stringify(obj.env.value);
    // env.name='';
    // env.name=obj.env.name;
    var container0 = {
        name: itemId,
        image: obj.image,
        imagePullPolicy: "Always",
  //      env: env
    }

    //self host not set resource
    if(self.id === 'default'){
        container0["resources"] = {
            limits: {
                cpu: (obj.cpu_shares * 1000).toString() + "m",
                memory: obj.mem_limit
            }
        };
    }


    if (obj.commands) {
        container0.command = obj.commands.split(" ");
    }
    rc.spec.template.spec.containers = [];
    rc.spec.template.spec.containers.push(container0);

    //set command
    if (obj.commands !== undefined && _commandMap.hasOwnProperty(obj.commands)) {
        rc.spec.template.spec.containers[0].command = _commandMap[obj.commands].slice();
    }

    //set args
    if (obj.args != undefined) {
        rc.spec.template.spec.containers[0].args = obj.args.slice();
    }

    //set ports
    rc.spec.template.spec.containers[0].ports = [];
    for (var j = 0; j < obj.ports.length; j++) {
        rc.spec.template.spec.containers[0].ports.push({
            "containerPort": parseInt(obj.ports[j].port),
            "protocol": obj.ports[j].protocol
        });
    }
    var env=[];
    for (var j = 0; j < obj.env.length; j++) {
        env[j]={};
        env[j].name=obj.env[j].name;
        env[j].value=obj.env[j].value;
    }
    rc.spec.template.spec.containers[0].env=[];
    rc.spec.template.spec.containers[0].env=env;
    //set pull image secret
    rc.spec.template.spec.imagePullSecrets = [];
    rc.spec.template.spec.imagePullSecrets.push({"name":"pullsecret"});

    //set restart policy
    rc.spec.template.spec.restartPolicy = obj.restart;

    //grace period sec
    rc.spec.template.spec.terminationGracePeriodSeconds = config.kubernetes.terminationGracePeriodSeconds;

    //set volume
    if( isHostPathSvc(svc) ){
        logger.debug("create a rc with volume type of hostPath") ;
        //设置hostPath类型volume的replication controller
        setHostPathRc(obj,rc) ;
    }else{

        if(config.storage.type === "glusterfs"){
            setGlusterfsRc(obj,rc);
        }else if(config.storage.type === "ceph"){
            setCephRc(obj,rc,svc.username);
        }
    }

    if(config.network){
        rc.spec.template.metadata.annotations={};
        rc.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity']='';
        var affinity={}
        affinity.nodeAffinity={};
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution={};
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms=[];
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]={};

        var matchExpressions=[];
        matchExpressions[0]={};
        matchExpressions[0].key='network';
        if(obj.isNetwork) {
            matchExpressions[0].operator = 'In';
        }else{
            matchExpressions[0].operator = 'NotIn';
        }
        matchExpressions[0].values=[];
        matchExpressions[0].values[0]=config.network;
        affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions=matchExpressions;
        rc.spec.template.metadata.annotations['scheduler.alpha.kubernetes.io/affinity']=JSON.stringify(affinity);
    }
    //set timezone sync
    if(obj.synctimezone){

        if(rc.spec.template.spec.containers[0].volumeMounts === undefined ){
            rc.spec.template.spec.containers[0].volumeMounts = [];
        }
        rc.spec.template.spec.containers[0].volumeMounts.push({
            "mountPath": '/etc/localtime',
            "name": 'timezone',
            "readOnly":true
        });


        if(rc.spec.template.spec.volumes === undefined){
            rc.spec.template.spec.volumes = [];
        }
        rc.spec.template.spec.volumes.push({
            "name": 'timezone',
            "hostPath": {
                "path": '/etc/localtime'
            }
        });

    }
    if(obj.hasOwnProperty("configmap")) {
        rc.spec.template.spec.volumes = rc.spec.template.spec.volumes.concat(obj.configmap.volumes);
        rc.spec.template.spec.containers[0].volumeMounts = rc.spec.template.spec.containers[0].volumeMounts.concat(obj.configmap.volumeMounts);
    }
    return rc;
}

function obj2Job(svc) {
    var _job = {
        "apiVersion": "batch/v1",
        "kind": "Job",
        "metadata": {
            "name": svc.app,
            "labels": {
                "app": svc.app
            }
        },
        "spec": {
            "selector": {
                "app": svc.app
            },
            "activeDeadlineSeconds": config.storage.serviceDeadlineSec,
            "template": {
                "metadata": {
                    "name": svc.app,
                    "labels": {
                        "app": svc.app
                    }
                },
                "spec": {
                    "containers": [
                        {
                            "name": svc.app,
                            "image": "naturecloud.io/library/volume-explorer",
                            "ports": [
                                {
                                    "containerPort": 80,
                                    "protocol": "TCP"
                                }
                            ],
                            "volumeMounts": [
                                {
                                    "name": "datavolume",
                                    "mountPath": "/data"
                                }
                            ],
                            "imagePullPolicy": "IfNotPresent"
                        }
                    ],
                    "restartPolicy": "Never"
                }
            }

        }
    };
    _job.spec.template.spec.volumes = setVolumesInJob(svc);
    return _job;
}

function setVolumesInJob(svc){
    var ret = [];
    if(svc.node.volumes[0].type === 'ceph'){
        ret = [
            {
                "name": "datavolume",
                "rbd": {
                    "monitors": config.cephMeta.monitors,
                    "pool": svc.username,
                    "image": svc.node.volumes[0].mountObj.volumeId,
                    "fsType": "ext4",
                    "readOnly": false
                }

            }
        ];
    }else if(svc.node.volumes[0].type === 'glusterfs' ||
             svc.node.volumes[0].type === undefined ){
        ret = [
            {
                "name": "datavolume",
                "glusterfs": {
                    "endpoints": "glusterfs-cluster",
                    "path": svc.node.volumes[0].mountObj.volumeId
                }
            }
        ];
    }
    return ret;
}

function getStorageEndPoint(ns) {
    var _obj = {};
    _obj = {
        "kind": "Endpoints",
        "apiVersion": "v1",
        "metadata": {
            "name": _glusterfsConf.serviceId,
            "namespace": ns
        },
        "subsets": []
    };

    _glusterfsConf.ip.forEach(function (ip) {
        _obj.subsets.push({
            "addresses": [
                {
                    "ip": ip
                }
            ],
            "ports": [
                {
                    "port": 1
                }
            ]
        });
    });

    return _obj;
}

function getStorageSvc(ns) {
    var _obj =
    {
        "kind": "Service",
        "apiVersion": "v1",
        "metadata": {
            "name": _glusterfsConf.serviceId,
            "namespace": ns
        },
        "spec": {
            "ports": [
                {"port": 1}
            ]
        }
    }
    return _obj;
}

function rollback(ctx, stack) {

    return new Promise(function (resolve, reject) {
        var promises = [];
        stack.reverse().forEach(function (exec) {
            var fn = exec.fn;
            result = result.then(fn.apply(ctx, exec.args));
            /*var fn = exec.fn;
             promises.push(fn.apply(ctx,exec.args));*/
        });


        result
            .then(function (finalText) {
                resolve(myRet(true));
            })
            .catch(function (error) {
                //TODO add failover
                reject(error);
            })
            .done();

    });


}
/**
 * create  volumes in the glusterfs
 * make sure the volume can be created once and next no need to create again.
 */
function createVolumes(ns, app, volumes, replicaNum) {
    return new Promise(function (resolve, reject) {
        var promises = [];
        var _createlist = [];

        //filter mounted volume
        /**
         *  stateful volume need to add create list, and check in creates stateful volume api
         *  scenarios:
         *  1. volume type change from default to stateful      default means isneed to create volume
         */
        volumes.forEach(function (volume) {
            /*if(volume.IsDefault){*/
            if(volume.type =="hostpath")
                return;
            if (volume.name === undefined || (!volume.isShared && volume.type !=="hostpath") )
                _createlist.push(volume);
            /*}*/
        });

        _createlist.forEach(function (one) {

            if (!one.isShared) {
                if(one.IsDefault){
                    promises.push(createStatefulVolume(ns, app, one, replicaNum));
                } else {
                    promises.push(createStatefulVolume(ns, app, one, replicaNum, volumes.length));
                }
                //var encodeMountPath = encodePathToVolname(one.mountTargetPath);
            } else {
                var _id = uuid(24, 16);
                promises.push(_storageHandle.createVolume(ns,{mountName:_id,volumeName:_id}));
            }
        });

        Promise.all(promises).then(function (retlst) {
            var _ret = true;
            retlst.forEach(function (one,index) {
                var vol = _createlist[index];
                /**
                 *  share volume
                 */
                if (vol.isShared) {
                    if (!one.ret)
                        _ret = false;
                    else {

                        //_createlist[index].IsDefault = false;
                        var _tmp_vol = _createlist[index];
                        for(var field in one.data){
                            _tmp_vol[field] = one.data[field];
                        }
                    }
                } else {
                    /**
                     *  discard mountArray
                     */

                    one.forEach(function (volData,_idx) {
                        if (!volData.ret)
                            _ret = false;
                        else{
                            //copy return data field to volume oral object
                            var _tmp_vol = _createlist[index];
                            // if(_idx > 0){
                                //var _tmp = Common.deepCopy(_tmp_vol);
                            if(volumes[index].IsDefault && _idx==0){
                                for(var field in volData.data){
                                    _tmp_vol[field] = volData.data[field];
                                }
                            } else {
                                volumes.push(volData.data);
                            }

                            // }else{
                            //     for(var field in volData.data){
                            //         _tmp_vol[field] = volData.data[field];
                            //     }
                            // }
                        }
                    });
                }
            });
            if (_ret)
                resolve(true);
            else
                reject('create vol error');

        }).catch(function (err) {
            reject(err);
        });

    });

}


/**
 * 格式化volume,此处只针对hostpath类型的
 */
function formatVolumes(ns, app, svc) {
    if(svc.volumeType!="hostPath"){
        return emptyPromise();
    } else {
        return new Promise( function ( res,rej ) {
            svc.node.volumes.forEach(function (one) {
                var _id = uuid(24, 16);
                if(one.volumeId){

                }else {
                    one.mountName = _id;
                    one.name = _id;
                    if (one.type === 'hostpath') {
                        var pat = config.highIOdir+'/' + ns + '/' + app + '/' + one.name + '-0';
                        one.volumeId = pat;
                    }
                }
            });
            setHostpath(svc,app,ns);
            // if(svc.rcnum!==1){
            //     var name=svc.node.volumes[0].name;
            //     for(var i=1;i<svc.rcnum;i++) {
            //         if (!svc.node.volumes[0].isShared) {
            //             svc.node.volumes[i] = {};
            //             svc.node.volumes[i].type = 'hostpath';
            //             svc.node.volumes[i].name = name + '-' + app + '-' + i;
            //             svc.node.volumes[i].mountName = name;
            //             svc.node.volumes[i].volumeId = config.highIOdir+'/' + ns + '/' + app + '/' + name + '-' + i;
            //         }
            //     }
            // }
            res(true) ;
        }) ;
    }
}
//for rollback ,delete volume in glusterfs
function deleteVolumes(ns, app, volumes, replicaNum) {
    return new Promise(function (resolve, reject) {

        var promises = [];

        volumes.forEach(function (volume) {
            var _id = volume.volumeId;
            promises.push(_storageHandle.deleteVolumeForce(ns,_id));
        });

        Promise.all(promises).then(function (retlst) {
            var _ret = true;
            retlst.forEach(function (one) {
                if (one instanceof Array) {
                    one.forEach(function (volData) {
                        if (!volData.ret)
                            _ret = false;
                    });
                } else {
                    if (!one)
                        _ret = false;
                }
            })
            if (_ret)
                resolve(true);
            else
                reject('delete vol error');

        }).catch(function (err) {
            reject(err)
        });

    });

}
/**
 * 1.create a endpoint if not exist
 * 2.create a service bridge for host and container
 */
function _createStorageSvcInOwnNS(_self, ns, volumes) {
    //var _self = this;
    return new Promise(function (resolve, reject) {
        if (volumes.length == 0)
            resolve(myRet(true));
        else {
            //check endpoint whether exist
            chkIsEndPointExist(_self, ns, _glusterfsConf.serviceId).then(function (ret) {
                //exist
                if (ret.ret)
                    resolve(myRet(true));
                // not exist
                else {
                    logger.info("no endpoint exist,then create one!")
                    var _rollback_stack = [];
                    //construct endpoint json object
                    var _storage_endpoint = getStorageEndPoint(ns);
                    //construct service json object
                    var _storage_svc = getStorageSvc(ns);

                    //create endpoint
                    createEndpoint(_self, ns, _storage_endpoint)
                    //create service
                        .then(function () {
                            _rollback_stack.push({
                                fn: deleteEndpoint,
                                args: [_self, ns, _storage_endpoint]
                            });
                            return createSvc(_self, ns, _storage_svc);
                        })
                        .then(function (ret) {
                            if (ret.ret)
                                resolve(myRet(true));
                            else
                                reject(myRet(false));
                        })
                        .catch(function (err) {

                            rollback(_self, _rollback_stack).then(function () {
                                reject(err);
                            }).catch(function (err) {
                                //rollback happened error
                                logger.info("createStorageSvcInOwnNS rollback err:" + err.error);
                                reject(err);
                            });
                        });
                }
            });
        }
    });
}

function _createCephSecrectInOwnNS(_self,ns){
    return new Promise(function (resolve, reject) {

    });
}

function updateVolumesStatus(svc, isUse) {
    if( !svc.node || !svc.node.volumes ) {
        return ;
    }
    return new Promise(function (resolve, reject) {
        var _vols = svc.node.volumes;
        var _list = [];

        _vols.forEach(function (one) {
            /**
             *  stateful volume should put volumeId from mountArray
             */
            _list.push(one.volumeId);
        });
        var _app =  svc.app;
        var callback = function (err, rets) {
            if (err)
                reject(myRet(false,err.message));
            else {
                //update object data after operator db
                svc.node.volumes.forEach(function (volume) {
                    //need to clean up data in object if delete  success from db
                    if(!isUse) {

                        if (volume.mountAppNameArray !== undefined) {
                            var _idx = volume.mountAppNameArray.indexOf(svc.app);
                            volume.mountAppNameArray.splice(_idx, 1);
                        }
                    }else{

                        if (volume.mountAppNameArray === undefined)
                            volume.mountAppNameArray = [];
                        volume.mountAppNameArray.push(_app);
                    }
                });
                resolve(myRet(true));
            }
        }

        var updateObj = {};
        if(isUse){
            updateObj = {
                $addToSet:{
                    mountAppNameArray:_app
                }
            }
        }else{
            updateObj = {
                $pull:{
                    mountAppNameArray:_app
                }
            }
        }


        storageInfoDB.updateMany(null, {
                namespace: svc.username,
                volumeId: {
                    $in: _list
                }
            },
            updateObj,
            callback
        );


    });
}
/**
 * insert a host path object into storageMetaInfo collection
 */
function insertHostPathInfo(svc,update){
    return new Promise(function(res,rej){
        //logger.debug( "svc == "+ JSON.stringify( svc )) ;
        var _vols = svc.node.volumes;
        if( update &&  svc.username ){
            //假如是修改，就把所有hostPath数据先删掉，在重新插入
            storageInfoDB.delWithParam(null,
                {
                    namespace:svc.username,
                    mountAppName:svc.app,
                    volumeType:"hostPath"
                },function (err, rets) {
                    logger.info( "storageInfoDB.delWithParam.ret="+JSON.stringify( rets )) ;
                    if (err) {
                        logger.error("storageInfoDB deleting failed "+svc.username+":"+svc.app) ;
                    } else {
                        logger.debug( "storageInfoDB deleting successfully "+svc.username+":"+svc.app ) ;
                    }

                    try{
                        //成功与否，都尝试插入数据
                        _vols.forEach(function (one) {
                            //_list.push(one.mountObj.volumeId);
                            //logger.debug( "insert db"+ JSON.stringify( one )) ;
                            storageInfoDB.insert(null, {
                                name :  one.mountObj.name,
                                volumeId : one.mountObj.volumeId,
                                namespace : svc.username,
                                volumeType:"hostPath",
                                mountAppName : svc.app,
                                mountNodeName : "",
                                size : 0
                            }, function (err,rets) {
                                if( !err ) err = {} ;
                                logger.info("mongo插入回调"+JSON.stringify( err ) ) ;
                            });

                        });
                    }catch(e){
                        logger.warn( JSON.stringify( e ) ) ;
                    }
                }//cb-end
            );
            res(true) ;
        }else{
           // logger.debug( JSON.stringify( _vols)) ;
            _vols.forEach(function (one) {
                //_list.push(one.mountObj.volumeId);
                //logger.debug( "insert db"+ JSON.stringify( one )) ;
                try{
                    storageInfoDB.insert(null, {
                        name :  one.mountObj.name,
                        volumeId : one.mountObj.volumeId,
                        namespace : svc.username,
                        volumeType:"hostPath",
                        pv : one.pv|| "null" ,
                        pvc : one.pvc|| "null" ,
                        mountAppName : svc.app,
                        mountNodeName : "",
                        size : 0
                    }, function (err,rets) {

                        logger.info("mongo插入回调"+JSON.stringify( err ) ) ;
                    });
                }catch(e){
                   logger.warn( JSON.stringify( e ) ) ;
                }
            });//each
            res(true) ;
        }
    }) ;
}
/**
 * 先把所有的数据都更新为NOT_USE，然后再把svc.node.volumes中存在的数据设置为USE状态
 * 这样一来，只有那些在本次修改中被删除的数据才会被设置为NOT_USE状态。
 */
function updateVolumeStatusExt(svc) {
    return new Promise(function (resolve, reject) {
        //every branch which must distinguish volume's type will be a disaster
        //so if there is time when you can not put up with the ugly code ,think about refactor plz
        formatVolumes(  svc.username, svc.app, svc).then(function () {
            return resetVolumeStatusInDB(svc) ;
        }) .then(function (ret) {
            if (ret.ret === true) {
                var _vols = svc.node.volumes;
                var _list = [];

                logger.debug("update volume :"+JSON.stringify( _vols) ) ;
                _vols.forEach(function (one) {
                    _list.push(one.volumeId);
                });
                var callback = function (err, rets) {
                    if (err)
                        reject(myRet(false,err.message));
                    else {
                        resolve(myRet(true));
                    }
                }

                var updateObj = {
                    $addToSet:{
                        mountAppNameArray:svc.app
                    }
                };

                storageInfoDB.updateMany(null, {
                        namespace: svc.username,
                        volumeId: {
                            $in: _list
                        }
                    },
                    updateObj,
                    callback
                );
            } else {
                reject(ret);
            }
        }).catch(function (err) {
            reject(err);
        })


    });

}
/**
 *  将storageInfo表状态设置为NOT_USE，mountAppName置空
 */
function resetVolumeStatusInDB(svc) {
    return new Promise(function (resolve, reject) {

        storageInfoDB.updateMany(null, {
                namespace: svc.username,
                mountAppName: svc.app
            },
            {
                $set: {
                    mountAppName: ''
                }
            },
            function (err, rets) {
                if (err){
                    logger.warn("resetVolumeStatusInDB#"+JSON.stringify(err)+",rets"+JSON.stringify( rets )) ;
                    reject(myRet(false,err.message));
                }else {

                    storageInfoDB.updateMany(null, {
                            namespace: svc.username,
                            //mountAppName: svc.app
                            mountAppNameArray: {
                                $in: [svc.app]
                            }
                        },
                        {
                            $pull: {
                                mountAppNameArray: svc.app
                            }
                        },
                        function (err, rets) {
                            if (err){
                                logger.warn("resetVolumeStatusInDB#"+JSON.stringify(err)+",rets"+JSON.stringify( rets )) ;
                                reject(myRet(false,err.message));
                            }else {
                                resolve(myRet(true));
                            }
                        }
                    );

                }
            }
        );
    });
}
/**
 * 清理无归属的  "volumeType" : "hostPath" 的 storageMetaInfo数据
 */
function clearHomeLessHostPathRecord(svc){
    storageInfoDB.delWithParam(null,
        {
            namespace:svc.username,
            mountAppName:"",
            volumeType:"hostPath"
        },function (err, rets) {
            //logger.debug( "clearHomeLessHostPathRecord.delWithParam.ret="+JSON.stringify( rets )) ;
            if (err) {
                logger.error("clearHomeLessHostPathRecord deleting failed "+svc.username+":"+svc.app) ;
            } else {
                logger.info( "clearHomeLessHostPathRecord deleting successfully "+svc.username+":"+svc.app ) ;
            }
        }//cb-end
    );
}

function _udpAppStatus(_self, ns, svc, status) {
    return new Promise(function (resolve, reject) {
        svcMetaInfoDB.updateField(null, {
            username: ns,
            app: svc
        }, {
            totalStatue: status
        }, function (err, rets) {
            if (err) {
                reject(err);
            } else {
                resolve(true);
            }
        });
    });
}
function _updateAutoStatus(_self,ns,svc,status){
    return new Promise(function (resolve,reject) {
        svcMetaInfoDB.updateorInsertField(null,{
            username:ns,
            app:svc
        },{
            updateEnabled:status
        }, function (err,rets) {
            if(err){
                reject(err);
            }else{
                resolve(true);
            }
        })
    })
}
function _updateScaleStatus(_self,ns,svc,data){
    return new Promise(function (resolve,reject) {
        svcMetaInfoDB.updateorInsertField(null,{
            username:ns,
            app:svc
        },{
            max:data.max,
            scalerEnabled:data.scalerEnabled,
            cpu:data.cpu,
            rcnum:data.svc.dbJson.rcnum
        }, function (err,rets) {
            if(err){
                reject(err);
            }else{
                resolve(true);
            }
        })
    })
}

function getJobObj(ns, volSvc) {
    return new Promise(function (resolve, reject) {
        storageInfoDB.getWithParam(null, {namespace: ns, svcId: volSvc}, function (err, rets) {
            if (err) {
                reject(err);
            } else {
                var _ret = undefined;
                if (rets.length > 0)
                    _ret = rets[0].job;
                resolve(_ret);
            }
        });
    });
}
function hostPath(path,app,seq){
    return uuid(24, 16) + "-" + app + "-" + seq;
}
function genPVCName(path, app, seq) {
    //return encodePathToVolname(path).slice(1) + "-" + app + "-" + seq;
    return uuid(24, 16) + "-" + app + "-" + seq;
}

function genPVName(path, app, seq) {
    //return encodePathToVolname(path).slice(1) + "-" + app + "-" + seq;
    return uuid(24, 16) + "-" + app + "-" + seq;
}

function encodePathToVolname(path) {
    return path.replace(/\//g,'-');
}
/**
 * inspect the method with transaction，warp the old method
 */
function withTransaction(username,svc,operation,closureProcedure ){
    if( closureProcedure===null ||  !(typeof closureProcedure === 'function') ){
        throw {
            name:"NullPointerException",
            message:"No closure procedure specified"
        }
    }
    return new Promise(function ( resolve,reject) {
        var bid = TxSupport().getBidFromSvc( operation+"#"+username , svc ) ,
            op = operation ,
            data = svc ;
        // apply transaction support
        TxSupport().begin( bid,op, data  ).then( function () {
            return closureProcedure( username,svc ) ;
        }).then(function ( ret ) {
            TxSupport().commit(bid) ;
            resolve( ret ) ;
        }).catch( function ( err ) {
            try{
                if( TxSupport().isObject( err ) && err.isFatal ){
                    //比较重大的问题，程序无法判断或者解决的需要人工处理的问题
                    TxSupport().setExceptionStatus( bid ) ;
                }else{
                    TxSupport().rollback( bid ) ;
                }
            }catch(e){
                err = myRet(false, e.message,e.message);
            }finally{
                reject( err ) ;
            }
        })
    }) ;
}
/**
 * adapter fo transactionManager
 * @constructor
 */
function TxSupport() {
    return {
        begin:function (bid ,op,data) {
            return txManager.begin( bid ,op,data ) ;
        },
        commit:function (bid) {
            return txManager.commit(bid ) ;
        },
        rollback:function ( bid ) {
            return txManager.rollback( bid ) ;
        },
        setExceptionStatus:function (bid) {
            txManager.setErrStatus( bid ) ;
            throw {
              name:"TxError",
              message:"An unhandlable error occurred"
            }; //无法再
        },
        getBidFromSvc:function(username ,svc){
            //通过用户名和id或者app作为唯一主键
            svc = svc || {} ;
            var rand = "" ;
            if( username.indexOf("create") === -1 ){
                rand =txManager.uuid() ; //除了创建和需要唯一，其他操作都可以重复操作多次
            }

            if( username && svc.app ){
                return username+":"+svc.app +rand ;
            }else if( username && svc.id){
                return username+":"+svc.id +rand ;
            }else{
                throw "No username or app name specified in svc object" ;
            }
        },
        isObject : function(obj){
            return Object.prototype.toString.call(obj) === '[object Object]';
        },
        rollBackFailure : function(msg){
            return {
                ret:false,
                data: msg ,
                isFatal:true,
                message:msg,
                name:"RollBackFailure"
            };
        }

    } ;
}
